CylinderShapeTest.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575
  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/Component/ComponentApplication.h>
  10. #include <AzCore/Math/Matrix3x3.h>
  11. #include <AzCore/Math/Random.h>
  12. #include <AzFramework/Components/TransformComponent.h>
  13. #include <Shape/CylinderShapeComponent.h>
  14. #include <AzCore/UnitTest/TestTypes.h>
  15. #include <ShapeThreadsafeTest.h>
  16. namespace UnitTest
  17. {
  18. class CylinderShapeTest
  19. : public LeakDetectionFixture
  20. {
  21. AZStd::unique_ptr<AZ::SerializeContext> m_serializeContext;
  22. AZStd::unique_ptr<AZ::ComponentDescriptor> m_transformComponentDescriptor;
  23. AZStd::unique_ptr<AZ::ComponentDescriptor> m_cylinderShapeComponentDescriptor;
  24. public:
  25. void SetUp() override
  26. {
  27. LeakDetectionFixture::SetUp();
  28. m_serializeContext = AZStd::make_unique<AZ::SerializeContext>();
  29. m_transformComponentDescriptor = AZStd::unique_ptr<AZ::ComponentDescriptor>(AzFramework::TransformComponent::CreateDescriptor());
  30. m_transformComponentDescriptor->Reflect(&(*m_serializeContext));
  31. m_cylinderShapeComponentDescriptor = AZStd::unique_ptr<AZ::ComponentDescriptor>(LmbrCentral::CylinderShapeComponent::CreateDescriptor());
  32. m_cylinderShapeComponentDescriptor->Reflect(&(*m_serializeContext));
  33. }
  34. void TearDown() override
  35. {
  36. m_transformComponentDescriptor.reset();
  37. m_cylinderShapeComponentDescriptor.reset();
  38. m_serializeContext.reset();
  39. LeakDetectionFixture::TearDown();
  40. }
  41. };
  42. using CylinderParams = std::tuple<AZ::Transform, float, float>;
  43. using BoundingBoxResult = std::tuple<AZ::Vector3, AZ::Vector3>;
  44. using BoundingBoxParams = std::tuple<CylinderParams, BoundingBoxResult>;
  45. using IsPointInsideParams = std::tuple<CylinderParams, AZ::Vector3, bool>;
  46. using RayParams = std::tuple<AZ::Vector3, AZ::Vector3>;
  47. using RayIntersectResult = std::tuple<bool, float, float>;
  48. using RayIntersectParams = std::tuple<RayParams, CylinderParams, RayIntersectResult>;
  49. using DistanceResultParams = std::tuple<float, float>;
  50. using DistanceFromPointParams = std::tuple<CylinderParams, AZ::Vector3, DistanceResultParams>;
  51. class CylinderShapeRayIntersectTest
  52. : public CylinderShapeTest
  53. , public testing::WithParamInterface<RayIntersectParams>
  54. {
  55. public:
  56. static const std::vector<RayIntersectParams> ShouldPass;
  57. static const std::vector<RayIntersectParams> ShouldFail;
  58. };
  59. class CylinderShapeAABBTest
  60. : public CylinderShapeTest
  61. , public testing::WithParamInterface<BoundingBoxParams>
  62. {
  63. public:
  64. static const std::vector<BoundingBoxParams> ShouldPass;
  65. };
  66. class CylinderShapeTransformAndLocalBoundsTest
  67. : public CylinderShapeTest
  68. , public testing::WithParamInterface<BoundingBoxParams>
  69. {
  70. public:
  71. static const std::vector<BoundingBoxParams> ShouldPass;
  72. };
  73. class CylinderShapeIsPointInsideTest
  74. : public CylinderShapeTest
  75. , public testing::WithParamInterface<IsPointInsideParams>
  76. {
  77. public:
  78. static const std::vector<IsPointInsideParams> ShouldPass;
  79. static const std::vector<IsPointInsideParams> ShouldFail;
  80. };
  81. class CylinderShapeDistanceFromPointTest
  82. : public CylinderShapeTest
  83. , public testing::WithParamInterface<DistanceFromPointParams>
  84. {
  85. public:
  86. static const std::vector<DistanceFromPointParams> ShouldPass;
  87. };
  88. const std::vector<RayIntersectParams> CylinderShapeRayIntersectTest::ShouldPass = {
  89. // Test case 0
  90. { // Ray: src, dir
  91. { AZ::Vector3(0.0f, 5.0f, 5.0f), AZ::Vector3(0.0f, -1.0f, 0.0f) },
  92. // Cylinder: transform, radius, height
  93. { AZ::Transform::CreateTranslation(
  94. AZ::Vector3(0.0f, 0.0f, 5.0f)),
  95. 0.5f, 5.0f },
  96. // Result: hit, distance, epsilon
  97. { true, 4.5f, 1e-4f } },
  98. // Test case 1
  99. { // Ray: src, dir
  100. { AZ::Vector3(-10.0f, -20.0f, 0.0f), AZ::Vector3(0.0f, 1.0f, 0.0f) },
  101. // Cylinder: transform, radius, height
  102. { AZ::Transform::CreateFromQuaternionAndTranslation(
  103. AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::HalfPi), AZ::Vector3(-10.0f, -10.0f, 0.0f)),
  104. 1.0f, 5.0f },
  105. // Result: hit, distance, epsilon
  106. { true, 7.5f, 1e-2f } },
  107. // Test case 2
  108. {// Ray: src, dir
  109. { AZ::Vector3(-10.0f, -10.0f, -10.0f), AZ::Vector3(0.0f, 0.0f, 1.0f) },
  110. // Cylinder: transform, radius, height
  111. { AZ::Transform::CreateFromQuaternionAndTranslation(
  112. AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::HalfPi), AZ::Vector3(-10.0f, -10.0f, 0.0f)),
  113. 1.0f, 5.0f },
  114. // Result: hit, distance, epsilon
  115. { true, 9.0f, 1e-2f } },
  116. // Test case 3
  117. {
  118. // Ray: src, dir
  119. { AZ::Vector3(-9.0f, -14.0f, -1.0f), AZ::Vector3(-1.0f, 0.0f, 0.0f) },
  120. // Cylinder: transform, radius, height
  121. { AZ::Transform::CreateTranslation(AZ::Vector3(-14.0f, -14.0f, -1.0f)) *
  122. AZ::Transform::CreateRotationY(AZ::Constants::HalfPi) *
  123. AZ::Transform::CreateRotationZ(AZ::Constants::HalfPi) *
  124. AZ::Transform::CreateUniformScale(4.0f),
  125. 1.0f, 1.25f },
  126. // Result: hit, distance, epsilon
  127. { true, 2.5f, 1e-2f }
  128. },
  129. // Test case 4
  130. { // Ray: src, dir
  131. { AZ::Vector3(0.0f, 5.0f, 5.0f), AZ::Vector3(0.0f, -1.0f, 0.0f) },
  132. // Cylinder: transform, radius, height
  133. { AZ::Transform::CreateTranslation(
  134. AZ::Vector3(0.0f, 0.0f, 5.0f)),
  135. 0.0f, 5.0f },
  136. // Result: hit, distance, epsilon
  137. { true, 0.0f, 1e-4f } },
  138. // Test case 5
  139. { // Ray: src, dir
  140. { AZ::Vector3(0.0f, 5.0f, 5.0f), AZ::Vector3(0.0f, -1.0f, 0.0f) },
  141. // Cylinder: transform, radius, height
  142. { AZ::Transform::CreateTranslation(
  143. AZ::Vector3(0.0f, 0.0f, 5.0f)),
  144. 0.0f, 5.0f },
  145. // Result: hit, distance, epsilon
  146. { true, 0.0f, 1e-4f } },
  147. // Test case 6
  148. { // Ray: src, dir
  149. { AZ::Vector3(0.0f, 5.0f, 5.0f), AZ::Vector3(0.0f, -1.0f, 0.0f) },
  150. // Cylinder: transform, radius, height
  151. { AZ::Transform::CreateTranslation(
  152. AZ::Vector3(0.0f, 0.0f, 5.0f)),
  153. 0.0f, 0.0f },
  154. // Result: hit, distance, epsilon
  155. { true, 0.0f, 1e-4f } }
  156. };
  157. const std::vector<RayIntersectParams> CylinderShapeRayIntersectTest::ShouldFail = {
  158. // Test case 0
  159. { // Ray: src, dir
  160. { AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3(1.0f, 0.0f, 0.0f) },
  161. // Cylinder: transform, radius, height
  162. { AZ::Transform::CreateTranslation(
  163. AZ::Vector3(0.0f, -10.0f, 0.0f)),
  164. 5.0f, 1.0f },
  165. // Result: hit, distance, epsilon
  166. { false, 0.0f, 0.0f } }
  167. };
  168. const std::vector<BoundingBoxParams> CylinderShapeAABBTest::ShouldPass = {
  169. // Test case 0
  170. { // Cylinder: transform, radius, height
  171. { AZ::Transform::CreateTranslation(
  172. AZ::Vector3(0.0f, -10.0f, 0.0f)),
  173. 5.0f, 1.0f },
  174. // AABB: min, max
  175. { AZ::Vector3(-5.0f, -15.0f, -0.5f), AZ::Vector3(5.0f, -5.0f, 0.5f) } },
  176. // Test case 1
  177. { // Cylinder: transform, radius, height
  178. { AZ::Transform::CreateTranslation(AZ::Vector3(-10.0f, -10.0f, 0.0f)) *
  179. AZ::Transform::CreateRotationX(AZ::Constants::HalfPi) *
  180. AZ::Transform::CreateRotationY(AZ::Constants::QuarterPi),
  181. 1.0f, 5.0f },
  182. // AABB: min, max
  183. { AZ::Vector3(-12.4748f, -12.4748f, -1.0f), AZ::Vector3(-7.52512f, -7.52512f, 1.0f) } },
  184. // Test case 2
  185. { // Cylinder: transform, radius, height
  186. { AZ::Transform::CreateTranslation(AZ::Vector3(-10.0f, -10.0f, 10.0f)) *
  187. AZ::Transform::CreateUniformScale(3.5f),
  188. 1.0f, 5.0f },
  189. // AABB: min, max
  190. { AZ::Vector3(-13.5f, -13.5f, 1.25f), AZ::Vector3(-6.5f, -6.5f, 18.75f) } },
  191. // Test case 3
  192. { // Cylinder: transform, radius, height
  193. { AZ::Transform::CreateTranslation(AZ::Vector3(0.0f, 0.0f, 0.0f)),
  194. 0.0f, 1.0f },
  195. // AABB: min, max
  196. { AZ::Vector3(0.0f, 0.0f, -0.5f), AZ::Vector3(0.0f, 0.0f,-0.5f) } },
  197. // Test case 4
  198. { // Cylinder: transform, radius, height
  199. { AZ::Transform::CreateTranslation(AZ::Vector3(0.0f, 0.0f, 0.0f)),
  200. 1.0f, 0.0f },
  201. // AABB: min, max
  202. { AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3(0.0f, 0.0f, 0.0f) } },
  203. // Test case 5
  204. { // Cylinder: transform, radius, height
  205. { AZ::Transform::CreateTranslation(AZ::Vector3(0.0f, 0.0f, 0.0f)),
  206. 0.0f, 0.0f },
  207. // AABB: min, max
  208. { AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3(0.0f, 0.0f, 0.0f) } }
  209. };
  210. const std::vector<BoundingBoxParams> CylinderShapeTransformAndLocalBoundsTest::ShouldPass = {
  211. // Test case 0
  212. { // Cylinder: transform, radius, height
  213. { AZ::Transform::CreateIdentity(),
  214. 5.0f, 1.0f },
  215. // Local bounds: min, max
  216. { AZ::Vector3(-5.0f, -5.0f, -0.5f), AZ::Vector3(5.0f, 5.0f, 0.5f) } },
  217. // Test case 1
  218. { // Cylinder: transform, radius, height
  219. { AZ::Transform::CreateTranslation(AZ::Vector3(-10.0f, -10.0f, 10.0f)) * AZ::Transform::CreateUniformScale(3.5f),
  220. 5.0f, 5.0f },
  221. // Local bounds: min, max
  222. { AZ::Vector3(-5.0f, -5.0f, -2.5f), AZ::Vector3(5.0f, 5.0f, 2.5f) } },
  223. // Test case 2
  224. { // Cylinder: transform, radius, height
  225. { AZ::Transform::CreateIdentity(),
  226. 0.0f, 5.0f },
  227. // Local bounds: min, max
  228. { AZ::Vector3(0.0f, 0.0f, -2.5f), AZ::Vector3(0.0f, 0.0f, 2.5f) } },
  229. // Test case 3
  230. { // Cylinder: transform, radius, height
  231. { AZ::Transform::CreateIdentity(),
  232. 5.0f, 0.0f },
  233. // Local bounds: min, max
  234. { AZ::Vector3(-5.0f, -5.0f, -0.0f), AZ::Vector3(5.0f, 5.0f, 0.0f) } },
  235. // Test case 4
  236. { // Cylinder: transform, radius, height
  237. { AZ::Transform::CreateIdentity(),
  238. 0.0f, 0.0f },
  239. // Local bounds: min, max
  240. { AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3(0.0f, 0.0f, 0.0f) } },
  241. };
  242. const std::vector<IsPointInsideParams> CylinderShapeIsPointInsideTest::ShouldPass = {
  243. // Test case 0
  244. { // Cylinder: transform, radius, height
  245. {AZ::Transform::CreateTranslation(AZ::Vector3(27.0f, 28.0f, 38.0f)) *
  246. AZ::Transform::CreateUniformScale(2.5f),
  247. 0.5f, 2.0f},
  248. // Point
  249. AZ::Vector3(27.0f, 28.5f, 40.0f),
  250. // Result
  251. true },
  252. // Test case 1
  253. { // Cylinder: transform, radius, height
  254. {AZ::Transform::CreateTranslation(AZ::Vector3(27.0f, 28.0f, 38.0f)) *
  255. AZ::Transform::CreateRotationX(AZ::Constants::HalfPi) *
  256. AZ::Transform::CreateRotationY(AZ::Constants::QuarterPi) *
  257. AZ::Transform::CreateUniformScale(0.5f),
  258. 0.5f, 2.0f},
  259. // Point
  260. AZ::Vector3(27.0f, 28.155f, 37.82f),
  261. // Result
  262. true }
  263. };
  264. const std::vector<IsPointInsideParams> CylinderShapeIsPointInsideTest::ShouldFail = {
  265. // Test case 0
  266. { // Cylinder: transform, radius, height
  267. {AZ::Transform::CreateTranslation(AZ::Vector3(0, 0.0f, 0.0f)),
  268. 0.0f, 1.0f},
  269. // Point
  270. AZ::Vector3(0.0f, 0.0f, 0.0f),
  271. // Result
  272. false },
  273. // Test case 1
  274. { // Cylinder: transform, radius, height
  275. {AZ::Transform::CreateTranslation(AZ::Vector3(0, 0.0f, 0.0f)),
  276. 1.0f, 0.0f},
  277. // Point
  278. AZ::Vector3(0.0f, 0.0f, 0.0f),
  279. // Result
  280. false },
  281. // Test case 2
  282. { // Cylinder: transform, radius, height
  283. {AZ::Transform::CreateTranslation(AZ::Vector3(0, 0.0f, 0.0f)),
  284. 0.0f, 0.0f},
  285. // Point
  286. AZ::Vector3(0.0f, 0.0f, 0.0f),
  287. // Result
  288. false }
  289. };
  290. const std::vector<DistanceFromPointParams> CylinderShapeDistanceFromPointTest::ShouldPass = {
  291. // Test case 0
  292. { // Cylinder: transform, radius, height
  293. { AZ::Transform::CreateTranslation(AZ::Vector3(27.0f, 28.0f, 38.0f)) *
  294. AZ::Transform::CreateRotationX(AZ::Constants::HalfPi) *
  295. AZ::Transform::CreateRotationY(AZ::Constants::QuarterPi) *
  296. AZ::Transform::CreateUniformScale(2.0f),
  297. 0.5f, 4.0f },
  298. // Point
  299. AZ::Vector3(27.0f, 28.0f, 41.0f),
  300. // Result: distance, epsilon
  301. { 2.0f, 1e-2f } },
  302. // Test case 1
  303. { // Cylinder: transform, radius, height
  304. { AZ::Transform::CreateTranslation(AZ::Vector3(27.0f, 28.0f, 38.0f)) *
  305. AZ::Transform::CreateRotationX(AZ::Constants::HalfPi) *
  306. AZ::Transform::CreateRotationY(AZ::Constants::QuarterPi) *
  307. AZ::Transform::CreateUniformScale(2.0f),
  308. 0.5f, 4.0f },
  309. // Point
  310. AZ::Vector3(22.757f, 32.243f, 38.0f),
  311. // Result: distance, epsilon
  312. { 2.0f, 1e-2f } },
  313. // Test case 2
  314. { // Cylinder: transform, radius, height
  315. { AZ::Transform::CreateTranslation(AZ::Vector3(0.0f, 0.0f, 0.0f)),
  316. 0.0f, 1.0f },
  317. // Point
  318. AZ::Vector3(0.0f, 5.0f, 0.0f),
  319. // Result: distance, epsilon
  320. { 5.0f, 1e-1f } },
  321. // Test case 3
  322. { // Cylinder: transform, radius, height
  323. { AZ::Transform::CreateTranslation(AZ::Vector3(0.0f, 0.0f, 0.0f)),
  324. 1.0f, 0.0f },
  325. // Point
  326. AZ::Vector3(0.0f, 5.0f, 0.0f),
  327. // Result: distance, epsilon
  328. { 5.0f, 1e-2f } },
  329. // Test case 4
  330. { // Cylinder: transform, radius, height
  331. { AZ::Transform::CreateTranslation(AZ::Vector3(0.0f, 0.0f, 0.0f)),
  332. 0.0f, 0.0f },
  333. // Point
  334. AZ::Vector3(0.0f, 5.0f, 0.0f),
  335. // Result: distance, epsilon
  336. { 5.0f, 1e-2f } }
  337. };
  338. void CreateCylinder(const AZ::Transform& transform, float radius, float height, AZ::Entity& entity)
  339. {
  340. entity.CreateComponent<LmbrCentral::CylinderShapeComponent>();
  341. entity.CreateComponent<AzFramework::TransformComponent>();
  342. entity.Init();
  343. entity.Activate();
  344. AZ::TransformBus::Event(entity.GetId(), &AZ::TransformBus::Events::SetWorldTM, transform);
  345. LmbrCentral::CylinderShapeComponentRequestsBus::Event(entity.GetId(), &LmbrCentral::CylinderShapeComponentRequestsBus::Events::SetHeight, height);
  346. LmbrCentral::CylinderShapeComponentRequestsBus::Event(entity.GetId(), &LmbrCentral::CylinderShapeComponentRequestsBus::Events::SetRadius, radius);
  347. }
  348. void CreateDefaultCylinder(const AZ::Transform& transform, AZ::Entity& entity)
  349. {
  350. CreateCylinder(transform, 10.0f, 10.0f, entity);
  351. }
  352. bool RandomPointsAreInCylinder(const AZ::RandomDistributionType distributionType)
  353. {
  354. //Apply a simple transform to put the Cylinder off the origin
  355. AZ::Transform transform = AZ::Transform::CreateIdentity();
  356. transform.SetRotation(AZ::Quaternion::CreateRotationY(AZ::Constants::HalfPi));
  357. transform.SetTranslation(AZ::Vector3(5.0f, 5.0f, 5.0f));
  358. AZ::Entity entity;
  359. CreateDefaultCylinder(transform, entity);
  360. const AZ::u32 testPoints = 10000;
  361. AZ::Vector3 testPoint;
  362. bool testPointInVolume = false;
  363. //Test a bunch of random points generated with the Normal random distribution type
  364. //They should all end up in the volume
  365. for (AZ::u32 i = 0; i < testPoints; ++i)
  366. {
  367. LmbrCentral::ShapeComponentRequestsBus::EventResult(testPoint, entity.GetId(), &LmbrCentral::ShapeComponentRequestsBus::Events::GenerateRandomPointInside, distributionType);
  368. LmbrCentral::ShapeComponentRequestsBus::EventResult(testPointInVolume, entity.GetId(), &LmbrCentral::ShapeComponentRequestsBus::Events::IsPointInside, testPoint);
  369. if (!testPointInVolume)
  370. {
  371. return false;
  372. }
  373. }
  374. return true;
  375. }
  376. TEST_F(CylinderShapeTest, NormalDistributionRandomPointsAreInVolume)
  377. {
  378. EXPECT_TRUE(RandomPointsAreInCylinder(AZ::RandomDistributionType::Normal));
  379. }
  380. TEST_F(CylinderShapeTest, UniformRealDistributionRandomPointsAreInVolume)
  381. {
  382. EXPECT_TRUE(RandomPointsAreInCylinder(AZ::RandomDistributionType::UniformReal));
  383. }
  384. TEST_P(CylinderShapeRayIntersectTest, GetRayIntersectCylinder)
  385. {
  386. const auto& [ray, cylinder, result] = GetParam();
  387. const auto& [src, dir] = ray;
  388. const auto& [transform, radius, height] = cylinder;
  389. const auto& [expectedHit, expectedDistance, epsilon] = result;
  390. AZ::Entity entity;
  391. CreateCylinder(transform, radius, height, entity);
  392. AZ::Transform::CreateFromQuaternionAndTranslation(
  393. AZ::Quaternion::CreateIdentity(), AZ::Vector3(0.0f, 0.0f, 5.0f));
  394. bool rayHit = false;
  395. float distance;
  396. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  397. rayHit, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay,
  398. src, dir, distance);
  399. EXPECT_EQ(rayHit, expectedHit);
  400. if (expectedHit)
  401. {
  402. EXPECT_NEAR(distance, expectedDistance, epsilon);
  403. }
  404. }
  405. INSTANTIATE_TEST_CASE_P(ValidIntersections,
  406. CylinderShapeRayIntersectTest,
  407. ::testing::ValuesIn(CylinderShapeRayIntersectTest::ShouldPass)
  408. );
  409. INSTANTIATE_TEST_CASE_P(InvalidIntersections,
  410. CylinderShapeRayIntersectTest,
  411. ::testing::ValuesIn(CylinderShapeRayIntersectTest::ShouldFail)
  412. );
  413. TEST_P(CylinderShapeAABBTest, GetAabb)
  414. {
  415. const auto& [cylinder, AABB] = GetParam();
  416. const auto& [transform, radius, height] = cylinder;
  417. const auto& [minExtent, maxExtent] = AABB;
  418. AZ::Entity entity;
  419. CreateCylinder(transform, radius, height, entity);
  420. AZ::Aabb aabb;
  421. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  422. aabb, entity.GetId(), &LmbrCentral::ShapeComponentRequests::GetEncompassingAabb);
  423. EXPECT_TRUE(aabb.GetMin().IsClose(minExtent));
  424. EXPECT_TRUE(aabb.GetMax().IsClose(maxExtent));
  425. }
  426. INSTANTIATE_TEST_CASE_P(AABB,
  427. CylinderShapeAABBTest,
  428. ::testing::ValuesIn(CylinderShapeAABBTest::ShouldPass)
  429. );
  430. TEST_P(CylinderShapeTransformAndLocalBoundsTest, GetTransformAndLocalBounds)
  431. {
  432. const auto& [cylinder, boundingBox] = GetParam();
  433. const auto& [transform, radius, height] = cylinder;
  434. const auto& [minExtent, maxExtent] = boundingBox;
  435. AZ::Entity entity;
  436. CreateCylinder(transform, radius, height, entity);
  437. AZ::Transform transformOut;
  438. AZ::Aabb aabb;
  439. LmbrCentral::ShapeComponentRequestsBus::Event(entity.GetId(), &LmbrCentral::ShapeComponentRequests::GetTransformAndLocalBounds, transformOut, aabb);
  440. EXPECT_TRUE(transformOut.IsClose(transform));
  441. EXPECT_TRUE(aabb.GetMin().IsClose(minExtent));
  442. EXPECT_TRUE(aabb.GetMax().IsClose(maxExtent));
  443. }
  444. INSTANTIATE_TEST_CASE_P(TransformAndLocalBounds,
  445. CylinderShapeTransformAndLocalBoundsTest,
  446. ::testing::ValuesIn(CylinderShapeTransformAndLocalBoundsTest::ShouldPass)
  447. );
  448. // point inside scaled
  449. TEST_P(CylinderShapeIsPointInsideTest, IsPointInside)
  450. {
  451. const auto& [cylinder, point, expectedInside] = GetParam();
  452. const auto& [transform, radius, height] = cylinder;
  453. AZ::Entity entity;
  454. CreateCylinder(transform, radius, height, entity);
  455. bool inside;
  456. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  457. inside, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IsPointInside, point);
  458. EXPECT_EQ(inside, expectedInside);
  459. }
  460. INSTANTIATE_TEST_CASE_P(ValidIsPointInside,
  461. CylinderShapeIsPointInsideTest,
  462. ::testing::ValuesIn(CylinderShapeIsPointInsideTest::ShouldPass)
  463. );
  464. INSTANTIATE_TEST_CASE_P(InvalidIsPointInside,
  465. CylinderShapeIsPointInsideTest,
  466. ::testing::ValuesIn(CylinderShapeIsPointInsideTest::ShouldFail)
  467. );
  468. // distance scaled - along length
  469. TEST_P(CylinderShapeDistanceFromPointTest, DistanceFromPoint)
  470. {
  471. const auto& [cylinder, point, result] = GetParam();
  472. const auto& [transform, radius, height] = cylinder;
  473. const auto& [expectedDistance, epsilon] = result;
  474. AZ::Entity entity;
  475. CreateCylinder(transform, radius, height, entity);
  476. float distance;
  477. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  478. distance, entity.GetId(), &LmbrCentral::ShapeComponentRequests::DistanceFromPoint, point);
  479. EXPECT_NEAR(distance, expectedDistance, epsilon);
  480. }
  481. INSTANTIATE_TEST_CASE_P(ValidIsDistanceFromPoint,
  482. CylinderShapeDistanceFromPointTest,
  483. ::testing::ValuesIn(CylinderShapeDistanceFromPointTest::ShouldPass)
  484. );
  485. TEST_F(CylinderShapeTest, ShapeHasThreadsafeGetSetCalls)
  486. {
  487. // Verify that setting values from one thread and querying values from multiple other threads in parallel produces
  488. // correct, consistent results.
  489. // Create our cylinder centered at 0 with our height and a starting radius.
  490. AZ::Entity entity;
  491. CreateCylinder(
  492. AZ::Transform::CreateTranslation(AZ::Vector3::CreateZero()), ShapeThreadsafeTest::MinDimension,
  493. ShapeThreadsafeTest::ShapeHeight, entity);
  494. // Define the function for setting unimportant dimensions on the shape while queries take place.
  495. auto setDimensionFn = [](AZ::EntityId shapeEntityId, float minDimension, uint32_t dimensionVariance, [[maybe_unused]] float height)
  496. {
  497. float radius = minDimension + aznumeric_cast<float>(rand() % dimensionVariance);
  498. LmbrCentral::CylinderShapeComponentRequestsBus::Event(
  499. shapeEntityId, &LmbrCentral::CylinderShapeComponentRequestsBus::Events::SetRadius, radius);
  500. };
  501. // Run the test, which will run multiple queries in parallel with each other and with the dimension-setting function.
  502. // The number of iterations is arbitrary - it's set high enough to catch most failures, but low enough to keep the test
  503. // time to a minimum.
  504. const int numIterations = 30000;
  505. ShapeThreadsafeTest::TestShapeGetSetCallsAreThreadsafe(entity, numIterations, setDimensionFn);
  506. }
  507. } // namespace UnitTest