CapsuleShapeTest.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571
  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 <AZTestShared/Math/MathTestHelpers.h>
  10. #include <AZTestShared/Utils/Utils.h>
  11. #include <AzCore/Component/ComponentApplication.h>
  12. #include <AzCore/UnitTest/TestTypes.h>
  13. #include <AzFramework/Components/TransformComponent.h>
  14. #include <AzFramework/UnitTest/TestDebugDisplayRequests.h>
  15. #include <Geometry/GeometrySystemComponent.h>
  16. #include <Shape/CapsuleShapeComponent.h>
  17. #include <ShapeTestUtils.h>
  18. #include <ShapeThreadsafeTest.h>
  19. namespace UnitTest
  20. {
  21. class CapsuleShapeTest
  22. : public LeakDetectionFixture
  23. {
  24. AZStd::unique_ptr<AZ::SerializeContext> m_serializeContext;
  25. AZStd::unique_ptr<AZ::ComponentDescriptor> m_transformComponentDescriptor;
  26. AZStd::unique_ptr<AZ::ComponentDescriptor> m_capsuleShapeComponentDescriptor;
  27. AZStd::unique_ptr<AZ::ComponentDescriptor> m_capsuleShapeDebugDisplayComponentDescriptor;
  28. AZStd::unique_ptr<AZ::ComponentDescriptor> m_geometrySystemComponentDescriptor;
  29. public:
  30. void SetUp() override
  31. {
  32. LeakDetectionFixture::SetUp();
  33. m_serializeContext = AZStd::make_unique<AZ::SerializeContext>();
  34. m_transformComponentDescriptor = AZStd::unique_ptr<AZ::ComponentDescriptor>(AzFramework::TransformComponent::CreateDescriptor());
  35. m_transformComponentDescriptor->Reflect(&(*m_serializeContext));
  36. m_capsuleShapeComponentDescriptor = AZStd::unique_ptr<AZ::ComponentDescriptor>(LmbrCentral::CapsuleShapeComponent::CreateDescriptor());
  37. m_capsuleShapeComponentDescriptor->Reflect(&(*m_serializeContext));
  38. m_capsuleShapeDebugDisplayComponentDescriptor = AZStd::unique_ptr<AZ::ComponentDescriptor>(LmbrCentral::CapsuleShapeDebugDisplayComponent::CreateDescriptor());
  39. m_capsuleShapeDebugDisplayComponentDescriptor->Reflect(&(*m_serializeContext));
  40. m_geometrySystemComponentDescriptor = AZStd::unique_ptr<AZ::ComponentDescriptor>(LmbrCentral::GeometrySystemComponent::CreateDescriptor());
  41. m_geometrySystemComponentDescriptor->Reflect(&(*m_serializeContext));
  42. }
  43. void TearDown() override
  44. {
  45. m_geometrySystemComponentDescriptor.reset();
  46. m_capsuleShapeDebugDisplayComponentDescriptor.reset();
  47. m_capsuleShapeComponentDescriptor.reset();
  48. m_transformComponentDescriptor.reset();
  49. m_serializeContext.reset();
  50. LeakDetectionFixture::TearDown();
  51. }
  52. };
  53. void CreateCapsule(
  54. AZ::Entity& entity,
  55. const AZ::Transform& transform,
  56. const float radius,
  57. const float height,
  58. const AZ::Vector3& translationOffset = AZ::Vector3::CreateZero())
  59. {
  60. entity.CreateComponent<LmbrCentral::CapsuleShapeComponent>();
  61. entity.CreateComponent<LmbrCentral::CapsuleShapeDebugDisplayComponent>();
  62. entity.CreateComponent<AzFramework::TransformComponent>();
  63. entity.Init();
  64. entity.Activate();
  65. AZ::TransformBus::Event(entity.GetId(), &AZ::TransformBus::Events::SetWorldTM, transform);
  66. LmbrCentral::CapsuleShapeComponentRequestsBus::Event(
  67. entity.GetId(), &LmbrCentral::CapsuleShapeComponentRequestsBus::Events::SetHeight, height);
  68. LmbrCentral::CapsuleShapeComponentRequestsBus::Event(
  69. entity.GetId(), &LmbrCentral::CapsuleShapeComponentRequestsBus::Events::SetRadius, radius);
  70. LmbrCentral::ShapeComponentRequestsBus::Event(
  71. entity.GetId(), &LmbrCentral::ShapeComponentRequestsBus::Events::SetTranslationOffset, translationOffset);
  72. }
  73. TEST_F(CapsuleShapeTest, GetRayIntersectCapsuleSuccess1)
  74. {
  75. AZ::Entity entity;
  76. CreateCapsule(
  77. entity,
  78. AZ::Transform::CreateFromQuaternionAndTranslation(AZ::Quaternion::CreateIdentity(), AZ::Vector3(0.0f, 0.0f, 5.0f)),
  79. 0.5f,
  80. 5.0f);
  81. bool rayHit = false;
  82. float distance;
  83. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  84. rayHit, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay,
  85. AZ::Vector3(0.0f, 5.0f, 5.0f), AZ::Vector3(0.0f, -1.0f, 0.0f), distance);
  86. EXPECT_TRUE(rayHit);
  87. EXPECT_NEAR(distance, 4.5f, 1e-4f);
  88. }
  89. TEST_F(CapsuleShapeTest, GetRayIntersectCapsuleSuccess2)
  90. {
  91. AZ::Entity entity;
  92. CreateCapsule(
  93. entity,
  94. AZ::Transform::CreateFromQuaternionAndTranslation(
  95. AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::HalfPi), AZ::Vector3(-10.0f, -10.0f, 0.0f)),
  96. 1.0f,
  97. 5.0f);
  98. bool rayHit = false;
  99. float distance;
  100. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  101. rayHit, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay,
  102. AZ::Vector3(-10.0f, -20.0f, 0.0f), AZ::Vector3(0.0f, 1.0f, 0.0f), distance);
  103. EXPECT_TRUE(rayHit);
  104. EXPECT_NEAR(distance, 7.5f, 1e-2f);
  105. }
  106. TEST_F(CapsuleShapeTest, GetRayIntersectCapsuleSuccess3)
  107. {
  108. AZ::Entity entity;
  109. CreateCapsule(
  110. entity,
  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,
  114. 5.0f);
  115. bool rayHit = false;
  116. float distance;
  117. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  118. rayHit, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay,
  119. AZ::Vector3(-10.0f, -10.0f, -10.0f), AZ::Vector3(0.0f, 0.0f, 1.0f), distance);
  120. EXPECT_TRUE(rayHit);
  121. EXPECT_NEAR(distance, 9.0f, 1e-2f);
  122. }
  123. // test sphere case
  124. TEST_F(CapsuleShapeTest, GetRayIntersectCapsuleSuccess4)
  125. {
  126. AZ::Entity entity;
  127. CreateCapsule(
  128. entity,
  129. AZ::Transform::CreateFromQuaternionAndTranslation(
  130. AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::HalfPi), AZ::Vector3(-10.0f, -10.0f, 0.0f)),
  131. 1.0f,
  132. 0.0f);
  133. bool rayHit = false;
  134. float distance;
  135. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  136. rayHit, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay,
  137. AZ::Vector3(-10.0f, -10.0f, -10.0f), AZ::Vector3(0.0f, 0.0f, 1.0f), distance);
  138. EXPECT_TRUE(rayHit);
  139. EXPECT_NEAR(distance, 9.0f, 1e-2f);
  140. }
  141. // transformed scaled
  142. TEST_F(CapsuleShapeTest, GetRayIntersectCapsuleSuccess5)
  143. {
  144. AZ::Entity entity;
  145. CreateCapsule(
  146. entity,
  147. AZ::Transform::CreateTranslation(AZ::Vector3(-4.0f, -12.0f, -3.0f)) * AZ::Transform::CreateRotationX(AZ::Constants::HalfPi) *
  148. AZ::Transform::CreateUniformScale(6.0f),
  149. 0.25f,
  150. 1.5f);
  151. bool rayHit = false;
  152. float distance;
  153. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  154. rayHit, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay,
  155. AZ::Vector3(-4.0f, -21.0f, -3.0f), AZ::Vector3(0.0f, 1.0f, 0.0f), distance);
  156. EXPECT_TRUE(rayHit);
  157. EXPECT_NEAR(distance, 4.5f, 1e-2f);
  158. }
  159. TEST_F(CapsuleShapeTest, GetRayIntersectCapsuleFailure)
  160. {
  161. AZ::Entity entity;
  162. CreateCapsule(
  163. entity,
  164. AZ::Transform::CreateFromQuaternionAndTranslation(AZ::Quaternion::CreateIdentity(), AZ::Vector3(0.0f, -10.0f, 0.0f)),
  165. 5.0f,
  166. 1.0f);
  167. bool rayHit = false;
  168. float distance;
  169. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  170. rayHit, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay,
  171. AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3(1.0f, 0.0f, 0.0f), distance);
  172. EXPECT_FALSE(rayHit);
  173. }
  174. TEST_F(CapsuleShapeTest, GetAabb1)
  175. {
  176. AZ::Entity entity;
  177. CreateCapsule(
  178. entity,
  179. AZ::Transform::CreateFromQuaternionAndTranslation(AZ::Quaternion::CreateIdentity(), AZ::Vector3(0.0f, -10.0f, 0.0f)),
  180. 5.0f,
  181. 1.0f);
  182. AZ::Aabb aabb;
  183. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  184. aabb, entity.GetId(), &LmbrCentral::ShapeComponentRequests::GetEncompassingAabb);
  185. EXPECT_TRUE(aabb.GetMin().IsClose(AZ::Vector3(-5.0f, -15.0f, -5.0f)));
  186. EXPECT_TRUE(aabb.GetMax().IsClose(AZ::Vector3(5.0f, -5.0f, 5.0f)));
  187. }
  188. TEST_F(CapsuleShapeTest, GetAabb2)
  189. {
  190. AZ::Entity entity;
  191. CreateCapsule(
  192. entity,
  193. AZ::Transform::CreateFromQuaternionAndTranslation(
  194. AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::HalfPi) *
  195. AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisY(), AZ::Constants::QuarterPi),
  196. AZ::Vector3(-10.0f, -10.0f, 0.0f)),
  197. 1.0f,
  198. 5.0f);
  199. AZ::Aabb aabb;
  200. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  201. aabb, entity.GetId(), &LmbrCentral::ShapeComponentRequests::GetEncompassingAabb);
  202. EXPECT_TRUE(aabb.GetMin().IsClose(AZ::Vector3(-12.0606f, -12.0606f, -1.0f)));
  203. EXPECT_TRUE(aabb.GetMax().IsClose(AZ::Vector3(-7.9393f, -7.9393f, 1.0f)));
  204. }
  205. // test with scale
  206. TEST_F(CapsuleShapeTest, GetAabb3)
  207. {
  208. AZ::Entity entity;
  209. CreateCapsule(entity, AZ::Transform::CreateUniformScale(3.5f), 2.0f, 4.0f);
  210. AZ::Aabb aabb;
  211. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  212. aabb, entity.GetId(), &LmbrCentral::ShapeComponentRequests::GetEncompassingAabb);
  213. EXPECT_TRUE(aabb.GetMin().IsClose(AZ::Vector3(-7.0f, -7.0f, -7.0f)));
  214. EXPECT_TRUE(aabb.GetMax().IsClose(AZ::Vector3(7.0f, 7.0f, 7.0f)));
  215. }
  216. // test with scale and translation
  217. TEST_F(CapsuleShapeTest, GetAabb4)
  218. {
  219. AZ::Entity entity;
  220. CreateCapsule(
  221. entity, AZ::Transform::CreateTranslation(AZ::Vector3(5.0f, 20.0f, 0.0f)) * AZ::Transform::CreateUniformScale(2.5f), 1.0f, 5.0f);
  222. AZ::Aabb aabb;
  223. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  224. aabb, entity.GetId(), &LmbrCentral::ShapeComponentRequests::GetEncompassingAabb);
  225. EXPECT_TRUE(aabb.GetMin().IsClose(AZ::Vector3(2.5f, 17.5f, -6.25f)));
  226. EXPECT_TRUE(aabb.GetMax().IsClose(AZ::Vector3(7.5f, 22.5f, 6.25f)));
  227. }
  228. TEST_F(CapsuleShapeTest, GetTransformAndLocalBounds1)
  229. {
  230. AZ::Entity entity;
  231. AZ::Transform transformIn = AZ::Transform::CreateIdentity();
  232. CreateCapsule(entity, transformIn, 5.0f, 2.0f);
  233. AZ::Transform transformOut;
  234. AZ::Aabb aabb;
  235. LmbrCentral::ShapeComponentRequestsBus::Event(entity.GetId(), &LmbrCentral::ShapeComponentRequests::GetTransformAndLocalBounds, transformOut, aabb);
  236. EXPECT_TRUE(transformOut.IsClose(transformIn));
  237. EXPECT_TRUE(aabb.GetMin().IsClose(AZ::Vector3(-5.0f, -5.0f, -5.0f)));
  238. EXPECT_TRUE(aabb.GetMax().IsClose(AZ::Vector3(5.0f, 5.0f, 5.0f)));
  239. }
  240. TEST_F(CapsuleShapeTest, GetTransformAndLocalBounds2)
  241. {
  242. AZ::Entity entity;
  243. AZ::Transform transformIn = AZ::Transform::CreateFromQuaternionAndTranslation(
  244. AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::HalfPi) *
  245. AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisY(), AZ::Constants::QuarterPi), AZ::Vector3(-10.0f, -10.0f, 0.0f));
  246. transformIn.MultiplyByUniformScale(3.0f);
  247. CreateCapsule(entity, transformIn, 5.0f, 2.0f);
  248. AZ::Transform transformOut;
  249. AZ::Aabb aabb;
  250. LmbrCentral::ShapeComponentRequestsBus::Event(entity.GetId(), &LmbrCentral::ShapeComponentRequests::GetTransformAndLocalBounds, transformOut, aabb);
  251. EXPECT_TRUE(transformOut.IsClose(transformIn));
  252. EXPECT_TRUE(aabb.GetMin().IsClose(AZ::Vector3(-5.0f, -5.0f, -5.0f)));
  253. EXPECT_TRUE(aabb.GetMax().IsClose(AZ::Vector3(5.0f, 5.0f, 5.0f)));
  254. }
  255. TEST_F(CapsuleShapeTest, GetTransformAndLocalBounds3)
  256. {
  257. AZ::Entity entity;
  258. AZ::Transform transformIn = AZ::Transform::CreateFromQuaternionAndTranslation(
  259. AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::HalfPi) *
  260. AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisY(), AZ::Constants::QuarterPi), AZ::Vector3(-10.0f, -10.0f, 0.0f));
  261. transformIn.MultiplyByUniformScale(3.0f);
  262. CreateCapsule(entity, transformIn, 2.0f, 5.0f);
  263. AZ::Transform transformOut;
  264. AZ::Aabb aabb;
  265. LmbrCentral::ShapeComponentRequestsBus::Event(entity.GetId(), &LmbrCentral::ShapeComponentRequests::GetTransformAndLocalBounds, transformOut, aabb);
  266. EXPECT_TRUE(transformOut.IsClose(transformIn));
  267. EXPECT_TRUE(aabb.GetMin().IsClose(AZ::Vector3(-2.0f, -2.0f, -2.5f)));
  268. EXPECT_TRUE(aabb.GetMax().IsClose(AZ::Vector3(2.0f, 2.0f, 2.5f)));
  269. }
  270. // point inside scaled
  271. TEST_F(CapsuleShapeTest, IsPointInsideSuccess1)
  272. {
  273. AZ::Entity entity;
  274. CreateCapsule(
  275. entity,
  276. AZ::Transform::CreateTranslation(AZ::Vector3(27.0f, 28.0f, 38.0f)) * AZ::Transform::CreateUniformScale(2.5f),
  277. 0.5f,
  278. 2.0f);
  279. bool inside;
  280. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  281. inside, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IsPointInside, AZ::Vector3(27.0f, 28.5f, 40.0f));
  282. EXPECT_TRUE(inside);
  283. }
  284. // point inside scaled
  285. TEST_F(CapsuleShapeTest, IsPointInsideSuccess2)
  286. {
  287. AZ::Entity entity;
  288. CreateCapsule(
  289. entity,
  290. AZ::Transform::CreateTranslation(AZ::Vector3(27.0f, 28.0f, 38.0f)) * AZ::Transform::CreateRotationX(AZ::Constants::HalfPi) *
  291. AZ::Transform::CreateRotationY(AZ::Constants::QuarterPi) * AZ::Transform::CreateUniformScale(0.5f),
  292. 0.5f,
  293. 2.0f);
  294. bool inside;
  295. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  296. inside, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IsPointInside, AZ::Vector3(27.0f, 28.155f, 37.82f));
  297. EXPECT_TRUE(inside);
  298. }
  299. // distance scaled - along length
  300. TEST_F(CapsuleShapeTest, DistanceFromPoint1)
  301. {
  302. AZ::Entity entity;
  303. CreateCapsule(
  304. entity,
  305. AZ::Transform::CreateTranslation(AZ::Vector3(27.0f, 28.0f, 38.0f)) * AZ::Transform::CreateRotationX(AZ::Constants::HalfPi) *
  306. AZ::Transform::CreateRotationY(AZ::Constants::QuarterPi) * AZ::Transform::CreateUniformScale(2.0f),
  307. 0.5f,
  308. 4.0f);
  309. float distance;
  310. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  311. distance, entity.GetId(), &LmbrCentral::ShapeComponentRequests::DistanceFromPoint, AZ::Vector3(27.0f, 28.0f, 41.0f));
  312. EXPECT_NEAR(distance, 2.0f, 1e-2f);
  313. }
  314. // distance scaled - from end
  315. TEST_F(CapsuleShapeTest, DistanceFromPoint2)
  316. {
  317. AZ::Entity entity;
  318. CreateCapsule(
  319. entity,
  320. AZ::Transform::CreateTranslation(AZ::Vector3(27.0f, 28.0f, 38.0f)) * AZ::Transform::CreateRotationX(AZ::Constants::HalfPi) *
  321. AZ::Transform::CreateRotationY(AZ::Constants::QuarterPi) * AZ::Transform::CreateUniformScale(2.0f),
  322. 0.5f,
  323. 4.0f);
  324. float distance;
  325. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  326. distance, entity.GetId(), &LmbrCentral::ShapeComponentRequests::DistanceFromPoint, AZ::Vector3(22.757f, 32.243f, 38.0f));
  327. EXPECT_NEAR(distance, 2.0f, 1e-2f);
  328. }
  329. TEST_F(CapsuleShapeTest, ShapeHasThreadsafeGetSetCalls)
  330. {
  331. // Verify that setting values from one thread and querying values from multiple other threads in parallel produces
  332. // correct, consistent results.
  333. // Create our capsule centered at 0 with our height and a starting radius.
  334. AZ::Entity entity;
  335. CreateCapsule(
  336. entity,
  337. AZ::Transform::CreateTranslation(AZ::Vector3::CreateZero()),
  338. ShapeThreadsafeTest::MinDimension,
  339. ShapeThreadsafeTest::ShapeHeight);
  340. // Define the function for setting unimportant dimensions on the shape while queries take place.
  341. auto setDimensionFn =
  342. [](AZ::EntityId shapeEntityId, float minDimension, uint32_t dimensionVariance, [[maybe_unused]] float height)
  343. {
  344. float radius = minDimension + aznumeric_cast<float>(rand() % dimensionVariance);
  345. LmbrCentral::CapsuleShapeComponentRequestsBus::Event(
  346. shapeEntityId, &LmbrCentral::CapsuleShapeComponentRequestsBus::Events::SetRadius, radius);
  347. };
  348. // Run the test, which will run multiple queries in parallel with each other and with the dimension-setting function.
  349. // The number of iterations is arbitrary - it's set high enough to catch most failures, but low enough to keep the test
  350. // time to a minimum.
  351. const int numIterations = 30000;
  352. ShapeThreadsafeTest::TestShapeGetSetCallsAreThreadsafe(entity, numIterations, setDimensionFn);
  353. }
  354. TEST_F(CapsuleShapeTest, GetRayIntersectCapsuleWithTranslationOffsetJustIntersecting)
  355. {
  356. AZ::Entity entity;
  357. CreateCapsule(
  358. entity,
  359. AZ::Transform(AZ::Vector3(7.0f, 8.0f, 9.0f), AZ::Quaternion(0.46f, 0.22f, 0.70f, 0.50f), 2.0f),
  360. 0.5f,
  361. 2.0f,
  362. AZ::Vector3(3.0f, 4.0f, 5.0f));
  363. bool rayHit = false;
  364. float distance;
  365. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  366. rayHit, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay,
  367. AZ::Vector3(13.0224f, 8.2928f, 24.0f), AZ::Vector3(0.0f, 0.0f, -1.0f), distance);
  368. EXPECT_TRUE(rayHit);
  369. EXPECT_NEAR(distance, 0.864f, 1e-3f);
  370. }
  371. TEST_F(CapsuleShapeTest, GetRayIntersectCapsuleWithTranslationOffsetJustMissing)
  372. {
  373. AZ::Entity entity;
  374. CreateCapsule(
  375. entity,
  376. AZ::Transform(AZ::Vector3(7.0f, 8.0f, 9.0f), AZ::Quaternion(0.46f, 0.22f, 0.70f, 0.50f), 2.0f),
  377. 0.5f,
  378. 2.0f,
  379. AZ::Vector3(3.0f, 4.0f, 5.0f));
  380. bool rayHit = false;
  381. float distance;
  382. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  383. rayHit, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay,
  384. AZ::Vector3(13.1f, 8.2928f, 24.0f), AZ::Vector3(0.0f, 0.0f, -1.0f), distance);
  385. EXPECT_FALSE(rayHit);
  386. }
  387. TEST_F(CapsuleShapeTest, GetAabbRotatedAndScaledWithTranslationOffset)
  388. {
  389. AZ::Entity entity;
  390. CreateCapsule(
  391. entity,
  392. AZ::Transform(AZ::Vector3(4.0f, -6.0f, 3.0f), AZ::Quaternion(0.1f, 0.7f, 0.1f, 0.7f), 2.2f),
  393. 0.8f,
  394. 3.0f,
  395. AZ::Vector3(7.0f, 4.0f, 2.0f));
  396. AZ::Aabb aabb;
  397. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  398. aabb, entity.GetId(), &LmbrCentral::ShapeComponentRequests::GetEncompassingAabb);
  399. EXPECT_THAT(aabb.GetMin(), IsClose(AZ::Vector3(5.1f, 5.0f, -11.08f)));
  400. EXPECT_THAT(aabb.GetMax(), IsClose(AZ::Vector3(11.7f, 8.52f, -7.56f)));
  401. }
  402. TEST_F(CapsuleShapeTest, GetTransformAndLocalBoundsWithTranslationOffset)
  403. {
  404. AZ::Entity entity;
  405. AZ::Transform transform(AZ::Vector3(-5.0f, -1.0f, 2.0f), AZ::Quaternion(0.46f, 0.26f, 0.58f, 0.62f), 1.7f);
  406. CreateCapsule(entity, transform, 0.6f, 3.5f, AZ::Vector3(-4.0f, 2.0f, 8.0f));
  407. AZ::Transform transformOut;
  408. AZ::Aabb aabb;
  409. LmbrCentral::ShapeComponentRequestsBus::Event(entity.GetId(), &LmbrCentral::ShapeComponentRequests::GetTransformAndLocalBounds, transformOut, aabb);
  410. EXPECT_THAT(transformOut, IsClose(transform));
  411. EXPECT_THAT(aabb.GetMin(), IsClose(AZ::Vector3(-4.6f, 1.4f, 6.25f)));
  412. EXPECT_THAT(aabb.GetMax(), IsClose(AZ::Vector3(-3.4f, 2.6f, 9.75f)));
  413. }
  414. TEST_F(CapsuleShapeTest, IsPointInsideWithTranslationOffset)
  415. {
  416. AZ::Entity entity;
  417. CreateCapsule(
  418. entity,
  419. AZ::Transform(AZ::Vector3(2.0f, 3.0f, -1.0f), AZ::Quaternion(0.48f, 0.36f, 0.48f, 0.64f), 0.8f),
  420. 0.5f,
  421. 4.0f,
  422. AZ::Vector3(-2.0f, -3.0f, 7.0f));
  423. // test some pairs of nearby points which should be just either side of the surface of the capsule
  424. EXPECT_TRUE(IsPointInside(entity, AZ::Vector3(5.9f, 0.1f, -2.0f)));
  425. EXPECT_FALSE(IsPointInside(entity, AZ::Vector3(5.8f, 0.1f, -2.0f)));
  426. EXPECT_TRUE(IsPointInside(entity, AZ::Vector3(8.8f, -0.55f, -1.4f)));
  427. EXPECT_FALSE(IsPointInside(entity, AZ::Vector3(8.9f, -0.55f, -1.4f)));
  428. EXPECT_TRUE(IsPointInside(entity, AZ::Vector3(7.48f, 0.15f, -1.74f)));
  429. EXPECT_FALSE(IsPointInside(entity, AZ::Vector3(7.49f, 0.15f, -1.74f)));
  430. }
  431. TEST_F(CapsuleShapeTest, DistanceFromPointWithTranslationOffset)
  432. {
  433. AZ::Entity entity;
  434. CreateCapsule(
  435. entity,
  436. AZ::Transform(AZ::Vector3(-4.0f, 2.0f, -3.0f), AZ::Quaternion(0.64f, 0.52f, 0.40f, 0.40f), 1.2f),
  437. 1.0f,
  438. 6.0f,
  439. AZ::Vector3(-1.0f, -1.0f, 5.0f));
  440. float distance = AZ::Constants::FloatMax;
  441. // should be inside
  442. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  443. distance, entity.GetId(), &LmbrCentral::ShapeComponentRequests::DistanceFromPoint, AZ::Vector3(1.0f, 0.4f, -6.4f));
  444. EXPECT_NEAR(distance, 0.0f, 1e-3f);
  445. // should be closest to end cap
  446. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  447. distance, entity.GetId(), &LmbrCentral::ShapeComponentRequests::DistanceFromPoint, AZ::Vector3(4.9952f, -0.0064f, -7.944f));
  448. EXPECT_NEAR(distance, 0.72f, 1e-3f);
  449. // should be closest to cylindrical section
  450. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  451. distance, entity.GetId(), &LmbrCentral::ShapeComponentRequests::DistanceFromPoint, AZ::Vector3(1.1672f, 1.6896f, -6.264f));
  452. EXPECT_NEAR(distance, 0.1f, 1e-3f);
  453. }
  454. TEST_F(CapsuleShapeTest, DebugDrawWithTranslationOffset)
  455. {
  456. AZ::Entity systemEntity;
  457. systemEntity.CreateComponent<LmbrCentral::GeometrySystemComponent>();
  458. systemEntity.Init();
  459. systemEntity.Activate();
  460. AZ::Entity entity;
  461. CreateCapsule(
  462. entity,
  463. AZ::Transform(AZ::Vector3(2.0f, 3.0f, 6.0f), AZ::Quaternion(0.32f, 0.16f, 0.16f, 0.92f), 0.8f),
  464. 2.0f,
  465. 7.0f,
  466. AZ::Vector3(2.0f, -2.0f, -3.0f));
  467. UnitTest::TestDebugDisplayRequests testDebugDisplayRequests;
  468. LmbrCentral::ShapeComponentNotificationsBus::Event(
  469. entity.GetId(),
  470. &LmbrCentral::ShapeComponentNotificationsBus::Events::OnShapeChanged,
  471. LmbrCentral::ShapeComponentNotifications::ShapeChangeReasons::ShapeChanged);
  472. AzFramework::EntityDebugDisplayEventBus::Event(entity.GetId(), &AzFramework::EntityDebugDisplayEvents::DisplayEntityViewport,
  473. AzFramework::ViewportInfo{ 0 }, testDebugDisplayRequests);
  474. const AZStd::vector<AZ::Vector3>& points = testDebugDisplayRequests.GetPoints();
  475. const AZ::Aabb debugDrawAabb = points.size() > 0 ? AZ::Aabb::CreatePoints(points.data(), points.size()) : AZ::Aabb::CreateNull();
  476. // use quite low tolerance because the debug draw mesh is only an approximation to a perfect capsule
  477. EXPECT_THAT(debugDrawAabb.GetMin(), IsCloseTolerance(AZ::Vector3(0.7f, 1.5f, 0.4f), 0.1f));
  478. EXPECT_THAT(debugDrawAabb.GetMax(), IsCloseTolerance(AZ::Vector3(4.9f, 6.0f, 5.4f), 0.1f));
  479. }
  480. }