PolygonPrismShapeTest.cpp 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951
  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/VertexContainerInterface.h>
  12. #include <AzFramework/Components/TransformComponent.h>
  13. #include <AzFramework/Components/NonUniformScaleComponent.h>
  14. #include <Shape/PolygonPrismShapeComponent.h>
  15. #include <AzCore/UnitTest/TestTypes.h>
  16. #include <AZTestShared/Math/MathTestHelpers.h>
  17. #include <ShapeThreadsafeTest.h>
  18. namespace UnitTest
  19. {
  20. class PolygonPrismShapeTest
  21. : public LeakDetectionFixture
  22. {
  23. AZStd::unique_ptr<AZ::SerializeContext> m_serializeContext;
  24. AZStd::unique_ptr<AZ::ComponentDescriptor> m_transformComponentDescriptor;
  25. AZStd::unique_ptr<AZ::ComponentDescriptor> m_polygonPrismShapeComponentDescriptor;
  26. AZStd::unique_ptr<AZ::ComponentDescriptor> m_nonUniformScaleComponentDescriptor;
  27. public:
  28. void SetUp() override
  29. {
  30. LeakDetectionFixture::SetUp();
  31. m_serializeContext = AZStd::make_unique<AZ::SerializeContext>();
  32. m_transformComponentDescriptor = AZStd::unique_ptr<AZ::ComponentDescriptor>(AzFramework::TransformComponent::CreateDescriptor());
  33. m_transformComponentDescriptor->Reflect(&(*m_serializeContext));
  34. m_polygonPrismShapeComponentDescriptor = AZStd::unique_ptr<AZ::ComponentDescriptor>(LmbrCentral::PolygonPrismShapeComponent::CreateDescriptor());
  35. m_polygonPrismShapeComponentDescriptor->Reflect(&(*m_serializeContext));
  36. m_nonUniformScaleComponentDescriptor = AZStd::unique_ptr<AZ::ComponentDescriptor>(AzFramework::NonUniformScaleComponent::CreateDescriptor());
  37. m_nonUniformScaleComponentDescriptor->Reflect(&(*m_serializeContext));
  38. }
  39. void TearDown() override
  40. {
  41. m_transformComponentDescriptor.reset();
  42. m_polygonPrismShapeComponentDescriptor.reset();
  43. m_nonUniformScaleComponentDescriptor.reset();
  44. m_serializeContext.reset();
  45. LeakDetectionFixture::TearDown();
  46. }
  47. };
  48. void CreatePolygonPrism(
  49. const AZ::Transform& transform, const float height, const AZStd::vector<AZ::Vector2>& vertices, AZ::Entity& entity)
  50. {
  51. entity.CreateComponent<LmbrCentral::PolygonPrismShapeComponent>();
  52. entity.CreateComponent<AzFramework::TransformComponent>();
  53. entity.Init();
  54. entity.Activate();
  55. AZ::TransformBus::Event(entity.GetId(), &AZ::TransformBus::Events::SetWorldTM, transform);
  56. LmbrCentral::PolygonPrismShapeComponentRequestBus::Event(entity.GetId(), &LmbrCentral::PolygonPrismShapeComponentRequests::SetHeight, height);
  57. LmbrCentral::PolygonPrismShapeComponentRequestBus::Event(entity.GetId(), &LmbrCentral::PolygonPrismShapeComponentRequests::SetVertices, vertices);
  58. }
  59. void CreatePolygonPrismWithNonUniformScale(
  60. const AZ::Transform& transform, const float height, const AZStd::vector<AZ::Vector2>& vertices,
  61. const AZ::Vector3& nonUniformScale, AZ::Entity& entity)
  62. {
  63. entity.CreateComponent<LmbrCentral::PolygonPrismShapeComponent>();
  64. entity.CreateComponent<AzFramework::TransformComponent>();
  65. entity.CreateComponent<AzFramework::NonUniformScaleComponent>();
  66. entity.Init();
  67. entity.Activate();
  68. AZ::TransformBus::Event(entity.GetId(), &AZ::TransformBus::Events::SetWorldTM, transform);
  69. AZ::NonUniformScaleRequestBus::Event(entity.GetId(), &AZ::NonUniformScaleRequests::SetScale, nonUniformScale);
  70. LmbrCentral::PolygonPrismShapeComponentRequestBus::Event(entity.GetId(), &LmbrCentral::PolygonPrismShapeComponentRequests::SetHeight, height);
  71. LmbrCentral::PolygonPrismShapeComponentRequestBus::Event(entity.GetId(), &LmbrCentral::PolygonPrismShapeComponentRequests::SetVertices, vertices);
  72. }
  73. TEST_F(PolygonPrismShapeTest, PolygonShapeComponent_IsPointInside)
  74. {
  75. AZ::Entity entity;
  76. CreatePolygonPrism(
  77. AZ::Transform::CreateIdentity(), 10.0f,
  78. AZStd::vector<AZ::Vector2>(
  79. {
  80. AZ::Vector2(0.0f, 0.0f),
  81. AZ::Vector2(0.0f, 10.0f),
  82. AZ::Vector2(10.0f, 10.0f),
  83. AZ::Vector2(10.0f, 0.0f)
  84. }),
  85. entity);
  86. // verify point inside returns true
  87. {
  88. bool inside;
  89. LmbrCentral::ShapeComponentRequestsBus::EventResult(inside, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IsPointInside, AZ::Vector3(5.0f, 5.0f, 5.0f));
  90. EXPECT_TRUE(inside);
  91. }
  92. // verify point outside return false
  93. {
  94. bool inside;
  95. LmbrCentral::ShapeComponentRequestsBus::EventResult(inside, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IsPointInside, AZ::Vector3(5.0f, 5.0f, 20.0f));
  96. EXPECT_TRUE(!inside);
  97. }
  98. // verify points at polygon edge return true
  99. {
  100. bool inside;
  101. LmbrCentral::ShapeComponentRequestsBus::EventResult(inside, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IsPointInside, AZ::Vector3(0.0f, 0.0f, 0.0f));
  102. EXPECT_TRUE(inside);
  103. }
  104. {
  105. bool inside;
  106. LmbrCentral::ShapeComponentRequestsBus::EventResult(inside, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IsPointInside, AZ::Vector3(0.0f, 10.0f, 0.0f));
  107. EXPECT_TRUE(inside);
  108. }
  109. {
  110. bool inside;
  111. LmbrCentral::ShapeComponentRequestsBus::EventResult(inside, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IsPointInside, AZ::Vector3(10.0f, 10.0f, 0.0f));
  112. EXPECT_TRUE(inside);
  113. }
  114. {
  115. bool inside;
  116. LmbrCentral::ShapeComponentRequestsBus::EventResult(inside, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IsPointInside, AZ::Vector3(10.0f, 0.0f, 0.0f));
  117. EXPECT_TRUE(inside);
  118. }
  119. {
  120. bool inside;
  121. LmbrCentral::ShapeComponentRequestsBus::EventResult(inside, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IsPointInside, AZ::Vector3(5.0f, 10.0f, 0.0f));
  122. EXPECT_TRUE(inside);
  123. }
  124. // verify point lies just inside
  125. {
  126. bool inside;
  127. LmbrCentral::ShapeComponentRequestsBus::EventResult(inside, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IsPointInside, AZ::Vector3(5.0f, 9.5f, 0.0f));
  128. EXPECT_TRUE(inside);
  129. }
  130. // verify point lies just outside
  131. {
  132. bool inside;
  133. LmbrCentral::ShapeComponentRequestsBus::EventResult(inside, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IsPointInside, AZ::Vector3(5.0f, 10.1f, 0.0f));
  134. EXPECT_FALSE(inside);
  135. }
  136. // note: the shape and positions/transforms were defined in the editor and replicated here - this
  137. // gave a good way to create various test cases and replicate them here
  138. AZ::TransformBus::Event(entity.GetId(), &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateFromMatrix3x3AndTranslation(AZ::Matrix3x3::CreateIdentity(), AZ::Vector3(497.0f, 595.0f, 32.0f)));
  139. LmbrCentral::PolygonPrismShapeComponentRequestBus::Event(entity.GetId(), &LmbrCentral::PolygonPrismShapeComponentRequests::SetVertices,
  140. AZStd::vector<AZ::Vector2>(
  141. {
  142. AZ::Vector2(0.0f, 9.0f),
  143. AZ::Vector2(6.5f, 6.5f),
  144. AZ::Vector2(9.0f, 0.0f),
  145. AZ::Vector2(6.5f, -6.5f),
  146. AZ::Vector2(0.0f, -9.0f),
  147. AZ::Vector2(-6.5f, -6.5f),
  148. AZ::Vector2(-9.0f, 0.0f),
  149. AZ::Vector2(-6.5f, 6.5f)
  150. }
  151. ));
  152. // verify point inside aabb but not inside polygon returns false
  153. {
  154. bool inside;
  155. LmbrCentral::ShapeComponentRequestsBus::EventResult(inside, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IsPointInside, AZ::Vector3(488.62f, 588.88f, 32.0f));
  156. EXPECT_FALSE(inside);
  157. }
  158. // verify point inside aabb and inside polygon returns true - when intersecting two vertices
  159. {
  160. bool inside;
  161. LmbrCentral::ShapeComponentRequestsBus::EventResult(inside, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IsPointInside, AZ::Vector3(496.62f, 595.0f, 32.0f));
  162. EXPECT_TRUE(inside);
  163. }
  164. LmbrCentral::PolygonPrismShapeComponentRequestBus::Event(entity.GetId(), &LmbrCentral::PolygonPrismShapeComponentRequests::SetVertices,
  165. AZStd::vector<AZ::Vector2>(
  166. {
  167. AZ::Vector2(0.0f, 0.0f),
  168. AZ::Vector2(10.0f, 0.0f),
  169. AZ::Vector2(5.0f, 10.0f)
  170. }
  171. ));
  172. {
  173. bool inside;
  174. LmbrCentral::ShapeComponentRequestsBus::EventResult(inside, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IsPointInside, AZ::Vector3(496.62f, 595.0f, 32.0f));
  175. EXPECT_FALSE(inside);
  176. }
  177. LmbrCentral::PolygonPrismShapeComponentRequestBus::Event(entity.GetId(), &LmbrCentral::PolygonPrismShapeComponentRequests::SetVertices,
  178. AZStd::vector<AZ::Vector2>(
  179. {
  180. AZ::Vector2(0.0f, 10.0f),
  181. AZ::Vector2(10.0f, 10.0f),
  182. AZ::Vector2(5.0f, 0.0f)
  183. }
  184. ));
  185. {
  186. bool inside;
  187. LmbrCentral::ShapeComponentRequestsBus::EventResult(inside, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IsPointInside, AZ::Vector3(496.62f, 595.0f, 32.0f));
  188. EXPECT_FALSE(inside);
  189. }
  190. LmbrCentral::PolygonPrismShapeComponentRequestBus::Event(entity.GetId(), &LmbrCentral::PolygonPrismShapeComponentRequests::SetVertices,
  191. AZStd::vector<AZ::Vector2>(
  192. {
  193. AZ::Vector2(0.0f, 0.0f),
  194. AZ::Vector2(10.0f, 0.0f),
  195. AZ::Vector2(5.0f, -10.0f)
  196. }
  197. ));
  198. {
  199. bool inside;
  200. LmbrCentral::ShapeComponentRequestsBus::EventResult(inside, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IsPointInside, AZ::Vector3(496.62f, 595.0f, 32.0f));
  201. EXPECT_FALSE(inside);
  202. }
  203. {
  204. bool inside;
  205. LmbrCentral::ShapeComponentRequestsBus::EventResult(inside, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IsPointInside, AZ::Vector3(502.0f, 585.1f, 32.0f));
  206. EXPECT_TRUE(inside);
  207. }
  208. {
  209. bool inside;
  210. LmbrCentral::ShapeComponentRequestsBus::EventResult(inside, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IsPointInside, AZ::Vector3(499.62f, 595.0f, 32.0f));
  211. EXPECT_TRUE(inside);
  212. }
  213. // U shape
  214. AZ::TransformBus::Event(entity.GetId(), &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateIdentity());
  215. LmbrCentral::PolygonPrismShapeComponentRequestBus::Event(entity.GetId(), &LmbrCentral::PolygonPrismShapeComponentRequests::SetVertices,
  216. AZStd::vector<AZ::Vector2>(
  217. {
  218. AZ::Vector2(0.0f, 0.0f),
  219. AZ::Vector2(0.0f, 10.0f),
  220. AZ::Vector2(5.0f, 10.0f),
  221. AZ::Vector2(5.0f, 5.0f),
  222. AZ::Vector2(10.0f, 5.0f),
  223. AZ::Vector2(10.0f, 10.0f),
  224. AZ::Vector2(15.0f, 15.0f),
  225. AZ::Vector2(15.0f, 0.0f),
  226. }
  227. ));
  228. {
  229. bool inside;
  230. LmbrCentral::ShapeComponentRequestsBus::EventResult(inside, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IsPointInside, AZ::Vector3(7.5f, 7.5f, 0.0f));
  231. EXPECT_FALSE(inside);
  232. }
  233. {
  234. bool inside;
  235. LmbrCentral::ShapeComponentRequestsBus::EventResult(inside, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IsPointInside, AZ::Vector3(12.5f, 7.5f, 0.0f));
  236. EXPECT_TRUE(inside);
  237. }
  238. {
  239. bool inside;
  240. LmbrCentral::ShapeComponentRequestsBus::EventResult(inside, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IsPointInside, AZ::Vector3(12.5f, 7.5f, 12.0f));
  241. EXPECT_FALSE(inside);
  242. }
  243. // check polygon prism with rotation
  244. AZ::TransformBus::Event(entity.GetId(), &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateRotationX(AZ::DegToRad(45.0f)));
  245. LmbrCentral::PolygonPrismShapeComponentRequestBus::Event(entity.GetId(), &LmbrCentral::PolygonPrismShapeComponentRequests::SetHeight, 10.0f);
  246. LmbrCentral::PolygonPrismShapeComponentRequestBus::Event(entity.GetId(), &LmbrCentral::PolygonPrismShapeComponentRequests::SetVertices,
  247. AZStd::vector<AZ::Vector2>(
  248. {
  249. AZ::Vector2(-5.0f, -5.0f),
  250. AZ::Vector2(-5.0f, 5.0f),
  251. AZ::Vector2(5.0f, 5.0f),
  252. AZ::Vector2(5.0f, -5.0f)
  253. }
  254. ));
  255. // check below
  256. {
  257. bool inside;
  258. LmbrCentral::ShapeComponentRequestsBus::EventResult(inside, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IsPointInside, AZ::Vector3(2.0f, 3.5f, 2.0f));
  259. EXPECT_FALSE(inside);
  260. }
  261. {
  262. bool inside;
  263. LmbrCentral::ShapeComponentRequestsBus::EventResult(inside, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IsPointInside, AZ::Vector3(2.0f, -8.0f, -2.0f));
  264. EXPECT_FALSE(inside);
  265. }
  266. // check above
  267. {
  268. bool inside;
  269. LmbrCentral::ShapeComponentRequestsBus::EventResult(inside, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IsPointInside, AZ::Vector3(2.0f, -8.0f, 8.0f));
  270. EXPECT_FALSE(inside);
  271. }
  272. {
  273. bool inside;
  274. LmbrCentral::ShapeComponentRequestsBus::EventResult(inside, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IsPointInside, AZ::Vector3(2.0f, 2.0f, 8.0f));
  275. EXPECT_FALSE(inside);
  276. }
  277. // check inside
  278. {
  279. bool inside;
  280. LmbrCentral::ShapeComponentRequestsBus::EventResult(inside, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IsPointInside, AZ::Vector3(2.0f, -3.0f, 8.0f));
  281. EXPECT_TRUE(inside);
  282. }
  283. {
  284. bool inside;
  285. LmbrCentral::ShapeComponentRequestsBus::EventResult(inside, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IsPointInside, AZ::Vector3(2.0f, -3.0f, -2.0f));
  286. EXPECT_TRUE(inside);
  287. }
  288. }
  289. TEST_F(PolygonPrismShapeTest, PolygonShapeComponent_IsPointInsideWithNonUniformScale)
  290. {
  291. AZ::Entity entity;
  292. AZ::Transform transform = AZ::Transform::CreateFromQuaternionAndTranslation(
  293. AZ::Quaternion::CreateRotationY(AZ::DegToRad(45.0f)), AZ::Vector3(3.0f, 4.0f, 5.0f));
  294. transform.MultiplyByUniformScale(1.5f);
  295. const float height = 1.2f;
  296. const AZ::Vector3 nonUniformScale(2.0f, 1.2f, 0.5f);
  297. const AZStd::vector<AZ::Vector2> vertices =
  298. {
  299. AZ::Vector2(1.0f, -1.0f),
  300. AZ::Vector2(2.0f, 0.0f),
  301. AZ::Vector2(-2.0f, 1.0f),
  302. AZ::Vector2(-1.0f, -1.0f)
  303. };
  304. CreatePolygonPrismWithNonUniformScale(transform, height, vertices, nonUniformScale, entity);
  305. // several points which should be outside the prism
  306. auto outsidePoints = {
  307. AZ::Vector3(4.0f, 5.0f, 4.5f),
  308. AZ::Vector3(1.0f, 1.0f, 7.5f),
  309. AZ::Vector3(7.5f, 3.0f, 2.5f),
  310. AZ::Vector3(-1.0, 6.0f, 11.0f),
  311. AZ::Vector3(2.0f, 4.0f, 5.5f),
  312. AZ::Vector3(4.0f, 3.5f, 5.5f)
  313. };
  314. // several points which should be just inside the prism
  315. auto insidePoints = {
  316. AZ::Vector3(0.0f, 5.5f, 9.0f),
  317. AZ::Vector3(1.5f, 2.5f, 7.5f),
  318. AZ::Vector3(5.5f, 2.5f, 3.75f),
  319. AZ::Vector3(7.75f, 4.0f, 1.5f),
  320. AZ::Vector3(2.5f, 3.0f, 5.6f),
  321. AZ::Vector3(4.0f, 4.5f, 5.25f)
  322. };
  323. for (const auto& point : outsidePoints)
  324. {
  325. bool inside = true;
  326. LmbrCentral::ShapeComponentRequestsBus::EventResult(inside, entity.GetId(),
  327. &LmbrCentral::ShapeComponentRequests::IsPointInside, point);
  328. EXPECT_FALSE(inside);
  329. }
  330. for (const auto& point : insidePoints)
  331. {
  332. bool inside = false;
  333. LmbrCentral::ShapeComponentRequestsBus::EventResult(inside, entity.GetId(),
  334. &LmbrCentral::ShapeComponentRequests::IsPointInside, point);
  335. EXPECT_TRUE(inside);
  336. }
  337. }
  338. TEST_F(PolygonPrismShapeTest, PolygonShapeComponent_DistanceFromPoint)
  339. {
  340. AZ::Entity entity;
  341. CreatePolygonPrism(
  342. AZ::Transform::CreateIdentity(),
  343. 10.0f, AZStd::vector<AZ::Vector2>(
  344. {
  345. AZ::Vector2(0.0f, 0.0f),
  346. AZ::Vector2(0.0f, 10.0f),
  347. AZ::Vector2(10.0f, 10.0f),
  348. AZ::Vector2(10.0f, 0.0f)
  349. }),
  350. entity);
  351. {
  352. float distance;
  353. LmbrCentral::ShapeComponentRequestsBus::EventResult(distance, entity.GetId(), &LmbrCentral::ShapeComponentRequests::DistanceFromPoint, AZ::Vector3(15.0f, 5.0f, 0.0f));
  354. EXPECT_TRUE(AZ::IsCloseMag(distance, 5.0f));
  355. }
  356. {
  357. float distance;
  358. LmbrCentral::ShapeComponentRequestsBus::EventResult(distance, entity.GetId(), &LmbrCentral::ShapeComponentRequests::DistanceFromPoint, AZ::Vector3(5.0f, 5.0f, 5.0f));
  359. EXPECT_TRUE(AZ::IsCloseMag(distance, 0.0f));
  360. }
  361. {
  362. float distance;
  363. LmbrCentral::ShapeComponentRequestsBus::EventResult(distance, entity.GetId(), &LmbrCentral::ShapeComponentRequests::DistanceFromPoint, AZ::Vector3(5.0f, 5.0f, 0.0f));
  364. EXPECT_TRUE(AZ::IsCloseMag(distance, 0.0f));
  365. }
  366. {
  367. float distance;
  368. LmbrCentral::ShapeComponentRequestsBus::EventResult(distance, entity.GetId(), &LmbrCentral::ShapeComponentRequests::DistanceFromPoint, AZ::Vector3(1.0f, 1.0f, -1.0f));
  369. EXPECT_TRUE(AZ::IsCloseMag(distance, 1.0f));
  370. }
  371. {
  372. float distance;
  373. LmbrCentral::ShapeComponentRequestsBus::EventResult(distance, entity.GetId(), &LmbrCentral::ShapeComponentRequests::DistanceFromPoint, AZ::Vector3(10.0f, 10.0f, 10.0f));
  374. EXPECT_TRUE(AZ::IsCloseMag(distance, 0.0f));
  375. }
  376. {
  377. float distance;
  378. LmbrCentral::ShapeComponentRequestsBus::EventResult(distance, entity.GetId(), &LmbrCentral::ShapeComponentRequests::DistanceFromPoint, AZ::Vector3(5.0f, 5.0f, 15.0f));
  379. EXPECT_TRUE(AZ::IsCloseMag(distance, 5.0f));
  380. }
  381. {
  382. float distance;
  383. LmbrCentral::ShapeComponentRequestsBus::EventResult(distance, entity.GetId(), &LmbrCentral::ShapeComponentRequests::DistanceFromPoint, AZ::Vector3(5.0f, 5.0f, -10.0f));
  384. EXPECT_TRUE(AZ::IsCloseMag(distance, 10.0f));
  385. }
  386. {
  387. float distance;
  388. LmbrCentral::ShapeComponentRequestsBus::EventResult(distance, entity.GetId(), &LmbrCentral::ShapeComponentRequests::DistanceFromPoint, AZ::Vector3(5.0f, 13.0f, 14.0f));
  389. EXPECT_TRUE(AZ::IsCloseMag(distance, 5.0f));
  390. }
  391. }
  392. TEST_F(PolygonPrismShapeTest, PolygonShapeComponent_DistanceFromPointWithNonUniformScale)
  393. {
  394. AZ::Entity entity;
  395. AZ::Transform transform = AZ::Transform::CreateFromQuaternionAndTranslation(
  396. AZ::Quaternion::CreateRotationY(AZ::DegToRad(45.0f)), AZ::Vector3(3.0f, 4.0f, 5.0f));
  397. transform.MultiplyByUniformScale(1.5f);
  398. const float height = 1.2f;
  399. const AZ::Vector3 nonUniformScale(2.0f, 1.2f, 0.5f);
  400. const AZStd::vector<AZ::Vector2> vertices =
  401. {
  402. AZ::Vector2(1.0f, -1.0f),
  403. AZ::Vector2(2.0f, 0.0f),
  404. AZ::Vector2(-2.0f, 1.0f),
  405. AZ::Vector2(-1.0f, -1.0f)
  406. };
  407. CreatePolygonPrismWithNonUniformScale(transform, height, vertices, nonUniformScale, entity);
  408. float distance = AZ::Constants::FloatMax;
  409. // a point which should be closest to one of the rectangular faces of the prism
  410. LmbrCentral::ShapeComponentRequestsBus::EventResult(distance, entity.GetId(),
  411. &LmbrCentral::ShapeComponentRequests::DistanceFromPoint, AZ::Vector3(4.0f, 5.0f, 4.5f));
  412. EXPECT_NEAR(distance, 0.2562f, 1e-3f);
  413. // a point which should be closest to one of the edges connecting the two polygonal faces
  414. LmbrCentral::ShapeComponentRequestsBus::EventResult(distance, entity.GetId(),
  415. &LmbrCentral::ShapeComponentRequests::DistanceFromPoint, AZ::Vector3(1.0f, 1.0f, 7.5f));
  416. EXPECT_NEAR(distance, 1.2137f, 1e-3f);
  417. // a point which should be closest to an edge of the top polygonal face
  418. LmbrCentral::ShapeComponentRequestsBus::EventResult(distance, entity.GetId(),
  419. &LmbrCentral::ShapeComponentRequests::DistanceFromPoint, AZ::Vector3(7.5f, 3.0f, 2.5f));
  420. EXPECT_NEAR(distance, 0.6041f, 1e-3f);
  421. // a point which should be closest to a corner of the top polygonal face
  422. LmbrCentral::ShapeComponentRequestsBus::EventResult(distance, entity.GetId(),
  423. &LmbrCentral::ShapeComponentRequests::DistanceFromPoint, AZ::Vector3(-1.0, 6.0f, 11.0f));
  424. EXPECT_NEAR(distance, 1.2048f, 1e-3f);
  425. // a point which should be closest to the bottom polygonal face
  426. LmbrCentral::ShapeComponentRequestsBus::EventResult(distance, entity.GetId(),
  427. &LmbrCentral::ShapeComponentRequests::DistanceFromPoint, AZ::Vector3(2.0f, 4.0f, 5.5f));
  428. EXPECT_NEAR(distance, 0.3536f, 1e-3f);
  429. // a point which should be closest to the top polygonal face
  430. LmbrCentral::ShapeComponentRequestsBus::EventResult(distance, entity.GetId(),
  431. &LmbrCentral::ShapeComponentRequests::DistanceFromPoint, AZ::Vector3(4.0f, 3.5f, 5.5f));
  432. EXPECT_NEAR(distance, 0.1607f, 1e-3f);
  433. // several points which should be just inside the prism
  434. auto insidePoints = {
  435. AZ::Vector3(0.0f, 5.5f, 9.0f),
  436. AZ::Vector3(1.5f, 2.5f, 7.5f),
  437. AZ::Vector3(5.5f, 2.5f, 3.75f),
  438. AZ::Vector3(7.75f, 4.0f, 1.5f),
  439. AZ::Vector3(2.5f, 3.0f, 5.6f),
  440. AZ::Vector3(4.0f, 4.5f, 5.25f)
  441. };
  442. for (const auto& point : insidePoints)
  443. {
  444. LmbrCentral::ShapeComponentRequestsBus::EventResult(distance, entity.GetId(),
  445. &LmbrCentral::ShapeComponentRequests::DistanceFromPoint, AZ::Vector3(point));
  446. EXPECT_NEAR(distance, 0.0f, 1e-3f);
  447. }
  448. }
  449. // ccw
  450. TEST_F(PolygonPrismShapeTest, GetRayIntersectPolygonPrismSuccess1)
  451. {
  452. AZ::Entity entity;
  453. CreatePolygonPrism(
  454. AZ::Transform::CreateIdentity(),
  455. 10.0f, AZStd::vector<AZ::Vector2>(
  456. {
  457. AZ::Vector2(0.0f, 0.0f),
  458. AZ::Vector2(0.0f, 10.0f),
  459. AZ::Vector2(10.0f, 10.0f),
  460. AZ::Vector2(10.0f, 0.0f)
  461. }),
  462. entity);
  463. bool rayHit = false;
  464. float distance;
  465. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  466. rayHit, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay,
  467. AZ::Vector3(5.0f, 5.0f, 15.0f), AZ::Vector3(0.0f, 0.0f, -1.0f), distance);
  468. EXPECT_TRUE(rayHit);
  469. EXPECT_NEAR(distance, 5.0f, 1e-2f);
  470. }
  471. // cw
  472. TEST_F(PolygonPrismShapeTest, GetRayIntersectPolygonPrismSuccess2)
  473. {
  474. AZ::Entity entity;
  475. CreatePolygonPrism(
  476. AZ::Transform::CreateIdentity(),
  477. 10.0f, AZStd::vector<AZ::Vector2>(
  478. {
  479. AZ::Vector2(0.0f, 0.0f),
  480. AZ::Vector2(10.0f, 0.0f),
  481. AZ::Vector2(10.0f, 10.0f),
  482. AZ::Vector2(0.0f, 10.0f),
  483. }),
  484. entity);
  485. bool rayHit = false;
  486. float distance;
  487. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  488. rayHit, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay,
  489. AZ::Vector3(5.0f, 5.0f, 15.0f), AZ::Vector3(0.0f, 0.0f, -1.0f), distance);
  490. EXPECT_TRUE(rayHit);
  491. EXPECT_NEAR(distance, 5.0f, 1e-2f);
  492. }
  493. TEST_F(PolygonPrismShapeTest, GetRayIntersectPolygonPrismSuccess3)
  494. {
  495. AZ::Entity entity;
  496. CreatePolygonPrism(
  497. AZ::Transform::CreateFromQuaternionAndTranslation(
  498. AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::HalfPi),
  499. AZ::Vector3(2.0f, 0.0f, 5.0f)),
  500. 2.0f,
  501. AZStd::vector<AZ::Vector2>(
  502. {
  503. AZ::Vector2(1.0f, 0.0f),
  504. AZ::Vector2(-1.0f, -2.0f),
  505. AZ::Vector2(-4.0f, -2.0f),
  506. AZ::Vector2(-6.0f, 0.0f),
  507. AZ::Vector2(-4.0f, 2.0f),
  508. AZ::Vector2(-1.0f, 2.0f)
  509. }),
  510. entity);
  511. {
  512. bool rayHit = false;
  513. float distance;
  514. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  515. rayHit, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay,
  516. AZ::Vector3(0.0f, 5.0f, 5.0f), AZ::Vector3(0.0f, -1.0f, 0.0f), distance);
  517. EXPECT_TRUE(rayHit);
  518. EXPECT_NEAR(distance, 5.0f, 1e-2f);
  519. }
  520. {
  521. bool rayHit = false;
  522. float distance;
  523. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  524. rayHit, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay,
  525. AZ::Vector3(0.0f, -1.0f, 9.0f), AZ::Vector3(0.0f, 0.0f, -1.0f), distance);
  526. EXPECT_TRUE(rayHit);
  527. EXPECT_NEAR(distance, 2.0f, 1e-2f);
  528. }
  529. }
  530. // transformed scaled
  531. TEST_F(PolygonPrismShapeTest, GetRayIntersectPolygonPrismSuccess4)
  532. {
  533. AZ::Entity entity;
  534. CreatePolygonPrism(
  535. AZ::Transform::CreateTranslation(AZ::Vector3(5.0f, 15.0f, 40.0f)) *
  536. AZ::Transform::CreateUniformScale(3.0f), 2.0f,
  537. AZStd::vector<AZ::Vector2>(
  538. {
  539. AZ::Vector2(-2.0f, -2.0f),
  540. AZ::Vector2(2.0f, -2.0f),
  541. AZ::Vector2(2.0f, 2.0f),
  542. AZ::Vector2(-2.0f, 2.0f)
  543. }),
  544. entity);
  545. {
  546. bool rayHit = false;
  547. float distance;
  548. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  549. rayHit, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay,
  550. AZ::Vector3(5.0f, 15.0f, 51.0f), AZ::Vector3(0.0f, 0.0f, -1.0f), distance);
  551. EXPECT_TRUE(rayHit);
  552. EXPECT_NEAR(distance, 5.0f, 1e-2f);
  553. }
  554. {
  555. bool rayHit = false;
  556. float distance;
  557. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  558. rayHit, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay,
  559. AZ::Vector3(15.0f, 15.0f, 43.0f), AZ::Vector3(-1.0f, 0.0f, 0.0f), distance);
  560. EXPECT_TRUE(rayHit);
  561. EXPECT_NEAR(distance, 4.0f, 1e-2f);
  562. }
  563. }
  564. TEST_F(PolygonPrismShapeTest, GetRayIntersectPolygonPrismFailure)
  565. {
  566. AZ::Entity entity;
  567. CreatePolygonPrism(
  568. AZ::Transform::CreateIdentity(),
  569. 1.0f, AZStd::vector<AZ::Vector2>(
  570. {
  571. AZ::Vector2(0.0f, 0.0f),
  572. AZ::Vector2(0.0f, 10.0f),
  573. AZ::Vector2(10.0f, 10.0f),
  574. AZ::Vector2(10.0f, 0.0f)
  575. }),
  576. entity);
  577. bool rayHit = false;
  578. float distance;
  579. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  580. rayHit, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay,
  581. AZ::Vector3(-3.0f, -1.0f, 2.0f), AZ::Vector3(1.0f, 0.0f, 0.0f), distance);
  582. EXPECT_FALSE(rayHit);
  583. }
  584. TEST_F(PolygonPrismShapeTest, GetRayIntersectWithNonUniformScale)
  585. {
  586. AZ::Entity entity;
  587. AZ::Transform transform = AZ::Transform::CreateFromQuaternionAndTranslation(
  588. AZ::Quaternion::CreateRotationY(AZ::DegToRad(60.0f)), AZ::Vector3(1.0f, 2.5f, -1.0f));
  589. transform.MultiplyByUniformScale(2.0f);
  590. const float height = 1.5f;
  591. const AZ::Vector3 nonUniformScale(0.5f, 1.5f, 2.0f);
  592. const AZStd::vector<AZ::Vector2> vertices = {
  593. AZ::Vector2(0.0f, -2.0f),
  594. AZ::Vector2(2.0f, 0.0f),
  595. AZ::Vector2(-1.0f, 2.0f)
  596. };
  597. CreatePolygonPrismWithNonUniformScale(transform, height, vertices, nonUniformScale, entity);
  598. // should hit one of the rectangular faces
  599. bool rayHit = false;
  600. AZ::Vector3 rayOrigin(3.0f, 3.0f, -3.0f);
  601. AZ::Vector3 rayDirection = AZ::Vector3::CreateAxisZ();
  602. float distance;
  603. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  604. rayHit, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay,
  605. rayOrigin, rayDirection, distance);
  606. EXPECT_TRUE(rayHit);
  607. EXPECT_NEAR(distance, 1.1340f, 1e-3f);
  608. // should hit a different rectangular face
  609. rayHit = false;
  610. rayOrigin = AZ::Vector3(2.0f, 2.0f, -3.0f);
  611. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  612. rayHit, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay,
  613. rayOrigin, rayDirection, distance);
  614. EXPECT_TRUE(rayHit);
  615. EXPECT_NEAR(distance, 0.4604f, 1e-3f);
  616. // should hit one of the triangular end faces
  617. rayHit = false;
  618. rayOrigin = AZ::Vector3(1.0f, 1.0f, -3.0f);
  619. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  620. rayHit, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay,
  621. rayOrigin, rayDirection, distance);
  622. EXPECT_TRUE(rayHit);
  623. EXPECT_NEAR(distance, 2.0f, 1e-3f);
  624. // should miss the prism
  625. rayHit = true;
  626. rayOrigin = AZ::Vector3(0.0f, 0.0f, -3.0f);
  627. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  628. rayHit, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay,
  629. rayOrigin, rayDirection, distance);
  630. EXPECT_FALSE(rayHit);
  631. }
  632. TEST_F(PolygonPrismShapeTest, PolygonShapeComponent_GetAabb1)
  633. {
  634. AZ::Entity entity;
  635. CreatePolygonPrism(
  636. AZ::Transform::CreateTranslation(AZ::Vector3(5.0f, 5.0f, 5.0f)), 10.0f,
  637. AZStd::vector<AZ::Vector2>(
  638. {
  639. AZ::Vector2(0.0f, 0.0f),
  640. AZ::Vector2(0.0f, 10.0f),
  641. AZ::Vector2(10.0f, 10.0f),
  642. AZ::Vector2(10.0f, 0.0f)
  643. }),
  644. entity);
  645. AZ::Aabb aabb;
  646. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  647. aabb, entity.GetId(), &LmbrCentral::ShapeComponentRequests::GetEncompassingAabb);
  648. EXPECT_TRUE(aabb.GetMin().IsClose(AZ::Vector3(5.0f, 5.0f, 5.0f)));
  649. EXPECT_TRUE(aabb.GetMax().IsClose(AZ::Vector3(15.0f, 15.0f, 15.0f)));
  650. }
  651. TEST_F(PolygonPrismShapeTest, PolygonShapeComponent_GetAabb2)
  652. {
  653. AZ::Entity entity;
  654. CreatePolygonPrism(
  655. AZ::Transform::CreateFromQuaternionAndTranslation(
  656. AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::QuarterPi) *
  657. AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisY(), AZ::Constants::QuarterPi),
  658. AZ::Vector3(5.0f, 15.0f, 20.0f)), 5.0f,
  659. AZStd::vector<AZ::Vector2>(
  660. {
  661. AZ::Vector2(-2.0f, -2.0f),
  662. AZ::Vector2(2.0f, -2.0f),
  663. AZ::Vector2(2.0f, 2.0f),
  664. AZ::Vector2(-2.0f, 2.0f)
  665. }),
  666. entity);
  667. AZ::Aabb aabb;
  668. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  669. aabb, entity.GetId(), &LmbrCentral::ShapeComponentRequests::GetEncompassingAabb);
  670. EXPECT_TRUE(aabb.GetMin().IsClose(AZ::Vector3(3.5857f, 10.08578f, 17.5857f)));
  671. EXPECT_TRUE(aabb.GetMax().IsClose(AZ::Vector3(9.9497f, 17.41413f, 24.9142f)));
  672. }
  673. // transformed scaled
  674. TEST_F(PolygonPrismShapeTest, PolygonShapeComponent_GetAabb3)
  675. {
  676. AZ::Entity entity;
  677. CreatePolygonPrism(
  678. AZ::Transform::CreateTranslation(AZ::Vector3(5.0f, 15.0f, 40.0f)) *
  679. AZ::Transform::CreateUniformScale(3.0f), 1.5f,
  680. AZStd::vector<AZ::Vector2>(
  681. {
  682. AZ::Vector2(-2.0f, -2.0f),
  683. AZ::Vector2(2.0f, -2.0f),
  684. AZ::Vector2(2.0f, 2.0f),
  685. AZ::Vector2(-2.0f, 2.0f)
  686. }),
  687. entity);
  688. AZ::Aabb aabb;
  689. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  690. aabb, entity.GetId(), &LmbrCentral::ShapeComponentRequests::GetEncompassingAabb);
  691. EXPECT_TRUE(aabb.GetMin().IsClose(AZ::Vector3(-1.0f, 9.0f, 40.0f)));
  692. EXPECT_TRUE(aabb.GetMax().IsClose(AZ::Vector3(11.0f, 21.0f, 44.5f)));
  693. }
  694. TEST_F(PolygonPrismShapeTest, PolygonShapeComponent_GetAabbWithNonUniformScale)
  695. {
  696. AZ::Entity entity;
  697. AZ::Transform transform = AZ::Transform::CreateFromQuaternionAndTranslation(
  698. AZ::Quaternion::CreateRotationX(AZ::DegToRad(30.0f)), AZ::Vector3(2.0f, -5.0f, 3.0f));
  699. transform.MultiplyByUniformScale(2.0f);
  700. const float height = 1.2f;
  701. const AZ::Vector3 nonUniformScale(1.5f, 0.8f, 2.0f);
  702. const AZStd::vector<AZ::Vector2> vertices =
  703. {
  704. AZ::Vector2(-2.0f, -2.0f),
  705. AZ::Vector2(1.0f, 0.0f),
  706. AZ::Vector2(2.0f, 3.0f),
  707. AZ::Vector2(-1.0f, 4.0f),
  708. AZ::Vector2(-3.0f, 2.0f)
  709. };
  710. CreatePolygonPrismWithNonUniformScale(transform, height, vertices, nonUniformScale, entity);
  711. AZ::Aabb aabb;
  712. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  713. aabb, entity.GetId(), &LmbrCentral::ShapeComponentRequests::GetEncompassingAabb);
  714. EXPECT_THAT(aabb.GetMin(), IsClose(AZ::Vector3(-7.0f, -10.171281f, 1.4f)));
  715. EXPECT_THAT(aabb.GetMax(), IsClose(AZ::Vector3(8.0f, 0.542563f, 10.356922f)));
  716. }
  717. TEST_F(PolygonPrismShapeTest, CopyingPolygonPrismDoesNotAssertInEbusSystem)
  718. {
  719. AZ::EntityId testEntityId{ 42 };
  720. LmbrCentral::PolygonPrismShape sourceShape;
  721. sourceShape.Activate(testEntityId);
  722. // The assignment shouldn't assert in the EBusEventHandler::BusConnect call
  723. LmbrCentral::PolygonPrismShape targetShape;
  724. AZ_TEST_START_TRACE_SUPPRESSION;
  725. targetShape = sourceShape;
  726. AZ_TEST_STOP_TRACE_SUPPRESSION(0);
  727. // The copy constructor also should assert
  728. AZ_TEST_START_TRACE_SUPPRESSION;
  729. LmbrCentral::PolygonPrismShape copyShape(sourceShape);
  730. AZ_TEST_STOP_TRACE_SUPPRESSION(0);
  731. sourceShape.Deactivate();
  732. }
  733. TEST_F(LeakDetectionFixture, PolygonPrismFilledMeshClearedWithLessThanThreeVertices)
  734. {
  735. // given
  736. // invalid vertex data (less than three vertices)
  737. const auto vertices = AZStd::vector<AZ::Vector2>{AZ::Vector2(0.0f, 0.0f), AZ::Vector2(1.0f, 0.0f)};
  738. // fill polygon prism mesh with some initial triangle data (to ensure it's cleared)
  739. LmbrCentral::PolygonPrismMesh polygonPrismMesh;
  740. polygonPrismMesh.m_triangles = AZStd::vector<AZ::Vector3>{
  741. AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3(0.0f, 1.0f, 0.0f), AZ::Vector3(1.0f, 0.0f, 0.0f)};
  742. // when
  743. const AZ::Vector3 nonUniformScale = AZ::Vector3::CreateOne();
  744. LmbrCentral::GeneratePolygonPrismMesh(vertices, 1.0f, nonUniformScale, polygonPrismMesh);
  745. // then
  746. EXPECT_TRUE(polygonPrismMesh.m_triangles.empty());
  747. }
  748. TEST_F(PolygonPrismShapeTest, ShapeHasThreadsafeGetSetCalls)
  749. {
  750. // Verify that setting values from one thread and querying values from multiple other threads in parallel produces
  751. // correct, consistent results.
  752. // Create our polygon prism centered at 0 with our height and a starting radius.
  753. AZ::Entity entity;
  754. const AZ::Vector2 baseVertices[] = { AZ::Vector2(-1.0f, -1.0f),
  755. AZ::Vector2( 1.0f, -1.0f),
  756. AZ::Vector2( 1.0f, 1.0f),
  757. AZ::Vector2(-1.0f, 1.0f)};
  758. CreatePolygonPrism(
  759. AZ::Transform::CreateTranslation(AZ::Vector3::CreateZero()), ShapeThreadsafeTest::ShapeHeight / 2.0f,
  760. AZStd::vector<AZ::Vector2>({ baseVertices[0] * ShapeThreadsafeTest::MinDimension,
  761. baseVertices[1] * ShapeThreadsafeTest::MinDimension,
  762. baseVertices[2] * ShapeThreadsafeTest::MinDimension,
  763. baseVertices[3] * ShapeThreadsafeTest::MinDimension }),
  764. entity);
  765. // Define the function for setting unimportant dimensions on the shape while queries take place.
  766. auto setDimensionFn = [baseVertices](AZ::EntityId shapeEntityId, float minDimension, uint32_t dimensionVariance, float height)
  767. {
  768. LmbrCentral::PolygonPrismShapeComponentRequestBus::Event(
  769. shapeEntityId, &LmbrCentral::PolygonPrismShapeComponentRequestBus::Events::SetHeight, height / 2.0f);
  770. AZ::PolygonPrismPtr polygonPrism;
  771. LmbrCentral::PolygonPrismShapeComponentRequestBus::EventResult(
  772. polygonPrism, shapeEntityId, &LmbrCentral::PolygonPrismShapeComponentRequestBus::Events::GetPolygonPrism);
  773. for (size_t index = 0; index < 4; index++)
  774. {
  775. float vertexScale = minDimension + aznumeric_cast<float>(rand() % dimensionVariance);
  776. polygonPrism->m_vertexContainer.UpdateVertex(index, baseVertices[index] * vertexScale);
  777. }
  778. };
  779. // Run the test, which will run multiple queries in parallel with each other and with the dimension-setting function.
  780. // The number of iterations is arbitrary - it's set high enough to catch most failures, but low enough to keep the test
  781. // time to a minimum.
  782. const int numIterations = 30000;
  783. ShapeThreadsafeTest::TestShapeGetSetCallsAreThreadsafe(entity, numIterations, setDimensionFn);
  784. }
  785. TEST_F(PolygonPrismShapeTest, StaleCallbacksAreNotCalledDuringActivation)
  786. {
  787. // If callbacks are set on the underlying polygon prism for the PolygonPrismShape Component, they should get cleared out
  788. // and reset on every deactivation / activation. There was previously a bug in which stale callbacks would get triggered
  789. // during the Activate call before getting cleared out at the end of Activate().
  790. // Create a simple polygon prism component.
  791. AZ::Entity entity;
  792. constexpr float ShapeHeight = 2.0f;
  793. CreatePolygonPrism(
  794. AZ::Transform::CreateTranslation(AZ::Vector3::CreateZero()), ShapeHeight,
  795. AZStd::vector<AZ::Vector2>(
  796. { AZ::Vector2(-2.0f, -2.0f), AZ::Vector2(2.0f, -2.0f), AZ::Vector2(2.0f, 2.0f), AZ::Vector2(-2.0f, 2.0f) }),
  797. entity);
  798. // Set the callbacks on the underlying polygonPrism shape so that we can detect when they get called.
  799. AZ::PolygonPrismPtr polygonPrism;
  800. LmbrCentral::PolygonPrismShapeComponentRequestBus::EventResult(
  801. polygonPrism, entity.GetId(), &LmbrCentral::PolygonPrismShapeComponentRequestBus::Events::GetPolygonPrism);
  802. int numCalls = 0;
  803. auto notificationCallback = [&numCalls]()
  804. {
  805. numCalls++;
  806. };
  807. auto vertexNotificationCallback = [&numCalls]([[maybe_unused]] size_t index)
  808. {
  809. numCalls++;
  810. };
  811. polygonPrism->SetCallbacks(
  812. vertexNotificationCallback, vertexNotificationCallback, vertexNotificationCallback,
  813. notificationCallback, notificationCallback, notificationCallback, notificationCallback);
  814. // Deactivate the component.
  815. entity.Deactivate();
  816. // No callbacks should have been triggered during the deactivate.
  817. EXPECT_EQ(numCalls, 0);
  818. // Activate the component.
  819. entity.Activate();
  820. // Our callbacks should not have been triggered during an activation.
  821. EXPECT_EQ(numCalls, 0);
  822. // Verify that setting the height at this point doesn't trigger our callbacks - they should have been reset back to default
  823. // during the component activation.
  824. polygonPrism->SetHeight(ShapeHeight + 1.0f);
  825. EXPECT_EQ(numCalls, 0);
  826. }
  827. } // namespace UnitTest