BoxShapeTest.cpp 46 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993
  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 <AZTestShared/Math/MathTestHelpers.h>
  9. #include <AZTestShared/Utils/Utils.h>
  10. #include <AzCore/Component/ComponentApplication.h>
  11. #include <AzCore/Math/Matrix3x3.h>
  12. #include <AzCore/Math/Random.h>
  13. #include <AzCore/UnitTest/TestTypes.h>
  14. #include <AzFramework/Components/NonUniformScaleComponent.h>
  15. #include <AzFramework/Components/TransformComponent.h>
  16. #include <AzFramework/UnitTest/TestDebugDisplayRequests.h>
  17. #include <AzTest/AzTest.h>
  18. #include <Shape/BoxShapeComponent.h>
  19. #include <ShapeTestUtils.h>
  20. #include <ShapeThreadsafeTest.h>
  21. namespace UnitTest
  22. {
  23. class BoxShapeTest
  24. : public LeakDetectionFixture
  25. {
  26. AZStd::unique_ptr<AZ::SerializeContext> m_serializeContext;
  27. AZStd::unique_ptr<AZ::ComponentDescriptor> m_transformComponentDescriptor;
  28. AZStd::unique_ptr<AZ::ComponentDescriptor> m_boxShapeComponentDescriptor;
  29. AZStd::unique_ptr<AZ::ComponentDescriptor> m_boxShapeDebugDisplayComponentDescriptor;
  30. AZStd::unique_ptr<AZ::ComponentDescriptor> m_nonUniformScaleComponentDescriptor;
  31. public:
  32. void SetUp() override
  33. {
  34. LeakDetectionFixture::SetUp();
  35. m_serializeContext = AZStd::make_unique<AZ::SerializeContext>();
  36. m_transformComponentDescriptor = AZStd::unique_ptr<AZ::ComponentDescriptor>(AzFramework::TransformComponent::CreateDescriptor());
  37. m_transformComponentDescriptor->Reflect(&(*m_serializeContext));
  38. m_boxShapeComponentDescriptor = AZStd::unique_ptr<AZ::ComponentDescriptor>(LmbrCentral::BoxShapeComponent::CreateDescriptor());
  39. m_boxShapeComponentDescriptor->Reflect(&(*m_serializeContext));
  40. m_boxShapeDebugDisplayComponentDescriptor = AZStd::unique_ptr<AZ::ComponentDescriptor>(
  41. LmbrCentral::BoxShapeDebugDisplayComponent::CreateDescriptor());
  42. m_boxShapeDebugDisplayComponentDescriptor->Reflect(&(*m_serializeContext));
  43. m_nonUniformScaleComponentDescriptor = AZStd::unique_ptr<AZ::ComponentDescriptor>(
  44. AzFramework::NonUniformScaleComponent::CreateDescriptor());
  45. m_nonUniformScaleComponentDescriptor->Reflect(&(*m_serializeContext));
  46. }
  47. void TearDown() override
  48. {
  49. m_transformComponentDescriptor.reset();
  50. m_boxShapeComponentDescriptor.reset();
  51. m_boxShapeDebugDisplayComponentDescriptor.reset();
  52. m_nonUniformScaleComponentDescriptor.reset();
  53. m_serializeContext.reset();
  54. LeakDetectionFixture::TearDown();
  55. }
  56. };
  57. void CreateBox(
  58. AZ::Entity& entity,
  59. const AZ::Transform& transform,
  60. const AZ::Vector3& dimensions,
  61. const AZ::Vector3& translationOffset = AZ::Vector3::CreateZero())
  62. {
  63. entity.CreateComponent<LmbrCentral::BoxShapeComponent>();
  64. entity.CreateComponent<LmbrCentral::BoxShapeDebugDisplayComponent>();
  65. entity.CreateComponent<AzFramework::TransformComponent>();
  66. entity.Init();
  67. entity.Activate();
  68. AZ::TransformBus::Event(entity.GetId(), &AZ::TransformBus::Events::SetWorldTM, transform);
  69. LmbrCentral::BoxShapeComponentRequestsBus::Event(
  70. entity.GetId(), &LmbrCentral::BoxShapeComponentRequestsBus::Events::SetBoxDimensions, dimensions);
  71. LmbrCentral::ShapeComponentRequestsBus::Event(
  72. entity.GetId(), &LmbrCentral::ShapeComponentRequestsBus::Events::SetTranslationOffset, translationOffset);
  73. }
  74. void CreateBoxWithNonUniformScale(
  75. AZ::Entity& entity,
  76. const AZ::Transform& transform,
  77. const AZ::Vector3& nonUniformScale,
  78. const AZ::Vector3& dimensions,
  79. const AZ::Vector3& translationOffset = AZ::Vector3::CreateZero())
  80. {
  81. entity.CreateComponent<LmbrCentral::BoxShapeComponent>();
  82. entity.CreateComponent<LmbrCentral::BoxShapeDebugDisplayComponent>();
  83. entity.CreateComponent<AzFramework::TransformComponent>();
  84. entity.CreateComponent<AzFramework::NonUniformScaleComponent>();
  85. entity.Init();
  86. entity.Activate();
  87. AZ::TransformBus::Event(entity.GetId(), &AZ::TransformBus::Events::SetWorldTM, transform);
  88. LmbrCentral::BoxShapeComponentRequestsBus::Event(
  89. entity.GetId(), &LmbrCentral::BoxShapeComponentRequestsBus::Events::SetBoxDimensions, dimensions);
  90. LmbrCentral::ShapeComponentRequestsBus::Event(
  91. entity.GetId(), &LmbrCentral::ShapeComponentRequestsBus::Events::SetTranslationOffset, translationOffset);
  92. AZ::NonUniformScaleRequestBus::Event(entity.GetId(), &AZ::NonUniformScaleRequests::SetScale, nonUniformScale);
  93. }
  94. void CreateDefaultBox(const AZ::Transform& transform, AZ::Entity& entity)
  95. {
  96. CreateBox(entity, transform, AZ::Vector3(10.0f, 10.0f, 10.0f));
  97. }
  98. bool RandomPointsAreInBox(const AZ::Entity& entity, const AZ::RandomDistributionType distributionType)
  99. {
  100. const size_t testPoints = 10000;
  101. AZ::Vector3 testPoint;
  102. bool testPointInVolume = false;
  103. // test a bunch of random points generated with a random distribution type
  104. // they should all end up in the volume
  105. for (size_t i = 0; i < testPoints; ++i)
  106. {
  107. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  108. testPoint, entity.GetId(), &LmbrCentral::ShapeComponentRequestsBus::Events::GenerateRandomPointInside, distributionType);
  109. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  110. testPointInVolume, entity.GetId(), &LmbrCentral::ShapeComponentRequestsBus::Events::IsPointInside, testPoint);
  111. if (!testPointInVolume)
  112. {
  113. return false;
  114. }
  115. }
  116. return true;
  117. }
  118. TEST_F(BoxShapeTest, NormalDistributionRandomPointsAreInAABB)
  119. {
  120. // don't rotate transform so that this is an AABB
  121. const AZ::Transform transform = AZ::Transform::CreateTranslation(AZ::Vector3(5.0f, 5.0f, 5.0f));
  122. AZ::Entity entity;
  123. CreateDefaultBox(transform, entity);
  124. const bool allRandomPointsInVolume = RandomPointsAreInBox(entity, AZ::RandomDistributionType::Normal);
  125. EXPECT_TRUE(allRandomPointsInVolume);
  126. }
  127. TEST_F(BoxShapeTest, UniformRealDistributionRandomPointsAreInAABB)
  128. {
  129. // don't rotate transform so that this is an AABB
  130. const AZ::Transform transform = AZ::Transform::CreateTranslation(AZ::Vector3(5.0f, 5.0f, 5.0f));
  131. AZ::Entity entity;
  132. CreateDefaultBox(transform, entity);
  133. const bool allRandomPointsInVolume = RandomPointsAreInBox(entity, AZ::RandomDistributionType::UniformReal);
  134. EXPECT_TRUE(allRandomPointsInVolume);
  135. }
  136. TEST_F(BoxShapeTest, NormalDistributionRandomPointsAreInOBB)
  137. {
  138. // rotate to end up with an OBB
  139. const AZ::Transform transform = AZ::Transform::CreateFromQuaternionAndTranslation(
  140. AZ::Quaternion::CreateRotationY(AZ::Constants::QuarterPi), AZ::Vector3(5.0f, 5.0f, 5.0f));
  141. AZ::Entity entity;
  142. CreateDefaultBox(transform, entity);
  143. const bool allRandomPointsInVolume = RandomPointsAreInBox(entity, AZ::RandomDistributionType::Normal);
  144. EXPECT_TRUE(allRandomPointsInVolume);
  145. }
  146. TEST_F(BoxShapeTest, UniformRealDistributionRandomPointsAreInOBB)
  147. {
  148. // rotate to end up with an OBB
  149. const AZ::Transform transform = AZ::Transform::CreateFromQuaternionAndTranslation(
  150. AZ::Quaternion::CreateRotationY(AZ::Constants::QuarterPi), AZ::Vector3(5.0f, 5.0f, 5.0f));
  151. AZ::Entity entity;
  152. CreateDefaultBox(transform, entity);
  153. const bool allRandomPointsInVolume = RandomPointsAreInBox(entity, AZ::RandomDistributionType::UniformReal);
  154. EXPECT_TRUE(allRandomPointsInVolume);
  155. }
  156. TEST_F(BoxShapeTest, UniformRealDistributionRandomPointsAreInAABBWithNonUniformScale)
  157. {
  158. AZ::Entity entity;
  159. const AZ::Transform transform = AZ::Transform::CreateTranslation(AZ::Vector3(2.0f, 6.0f, -3.0f));
  160. const AZ::Vector3 dimensions(2.4f, 1.2f, 0.6f);
  161. const AZ::Vector3 nonUniformScale(0.2f, 0.3f, 0.1f);
  162. CreateBoxWithNonUniformScale(entity, transform, nonUniformScale, dimensions);
  163. const bool allRandomPointsInVolume = RandomPointsAreInBox(entity, AZ::RandomDistributionType::UniformReal);
  164. EXPECT_TRUE(allRandomPointsInVolume);
  165. }
  166. TEST_F(BoxShapeTest, UniformRealDistributionRandomPointsAreInOBBWithNonUniformScale)
  167. {
  168. AZ::Entity entity;
  169. const AZ::Transform transform = AZ::Transform::CreateFromQuaternionAndTranslation(
  170. AZ::Quaternion(0.48f, 0.60f, 0.0f, 0.64f), AZ::Vector3(2.0f, 6.0f, -3.0f));
  171. const AZ::Vector3 dimensions(1.5f, 2.2f, 1.6f);
  172. const AZ::Vector3 nonUniformScale(0.4f, 0.1f, 0.3f);
  173. CreateBoxWithNonUniformScale(entity, transform, nonUniformScale, dimensions);
  174. const bool allRandomPointsInVolume = RandomPointsAreInBox(entity, AZ::RandomDistributionType::UniformReal);
  175. EXPECT_TRUE(allRandomPointsInVolume);
  176. }
  177. TEST_F(BoxShapeTest, UniformRealDistributionRandomPointsAreInAABBWithNonUniformScaleAndEntityScale)
  178. {
  179. AZ::Entity entity;
  180. AZ::Transform transform = AZ::Transform::CreateTranslation(AZ::Vector3(0.5f, -3.0f, 4.0f));
  181. transform.SetUniformScale(2.0f);
  182. const AZ::Vector3 dimensions(2.5f, 1.8f, 0.9f);
  183. const AZ::Vector3 nonUniformScale(0.6f, 0.5f, 0.2f);
  184. CreateBoxWithNonUniformScale(entity, transform, nonUniformScale, dimensions);
  185. const bool allRandomPointsInVolume = RandomPointsAreInBox(entity, AZ::RandomDistributionType::UniformReal);
  186. EXPECT_TRUE(allRandomPointsInVolume);
  187. }
  188. TEST_F(BoxShapeTest, UniformRealDistributionRandomPointsAreInOBBWithNonUniformScaleAndEntityScale)
  189. {
  190. AZ::Entity entity;
  191. AZ::Transform transform =
  192. AZ::Transform::CreateFromQuaternionAndTranslation(AZ::Quaternion(0.52f, 0.08f, 0.56f, 0.64f), AZ::Vector3(-2.0f, 1.0f, -2.0f));
  193. transform.SetUniformScale(1.5f);
  194. const AZ::Vector3 dimensions(3.2f, 2.6f, 1.3f);
  195. const AZ::Vector3 nonUniformScale(0.7f, 0.3f, 0.6f);
  196. CreateBoxWithNonUniformScale(entity, transform, nonUniformScale, dimensions);
  197. const bool allRandomPointsInVolume = RandomPointsAreInBox(entity, AZ::RandomDistributionType::UniformReal);
  198. EXPECT_TRUE(allRandomPointsInVolume);
  199. }
  200. TEST_F(BoxShapeTest, GetRayIntersectBoxSuccess1)
  201. {
  202. AZ::Entity entity;
  203. CreateBox(
  204. entity,
  205. AZ::Transform::CreateTranslation(AZ::Vector3(0.0f, 0.0f, 5.0f)) * AZ::Transform::CreateRotationZ(AZ::Constants::QuarterPi),
  206. AZ::Vector3(1.0f));
  207. bool rayHit = false;
  208. float distance;
  209. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  210. rayHit, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay,
  211. AZ::Vector3(0.0f, 5.0f, 5.0f), AZ::Vector3(0.0f, -1.0f, 0.0f), distance);
  212. // 5.0 - 0.707 ~= 4.29 (box rotated by 45 degrees)
  213. EXPECT_TRUE(rayHit);
  214. EXPECT_NEAR(distance, 4.29f, 1e-2f);
  215. }
  216. TEST_F(BoxShapeTest, GetRayIntersectBoxSuccess2)
  217. {
  218. AZ::Entity entity;
  219. CreateBox(
  220. entity,
  221. AZ::Transform::CreateFromQuaternionAndTranslation(
  222. AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::HalfPi) *
  223. AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisZ(), AZ::Constants::QuarterPi),
  224. AZ::Vector3(-10.0f, -10.0f, -10.0f)),
  225. AZ::Vector3(4.0f, 4.0f, 2.0f));
  226. bool rayHit = false;
  227. float distance;
  228. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  229. rayHit, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay,
  230. AZ::Vector3(-10.0f, -10.0f, 0.0f), AZ::Vector3(0.0f, 0.0f, -1.0f), distance);
  231. // 0.70710678 * 4 = 2.8284271
  232. // 10.0 - 2.8284271 ~= 7.17157287
  233. EXPECT_TRUE(rayHit);
  234. EXPECT_NEAR(distance, 7.17f, 1e-2f);
  235. }
  236. TEST_F(BoxShapeTest, GetRayIntersectBoxSuccess3)
  237. {
  238. AZ::Entity entity;
  239. CreateBox(
  240. entity,
  241. AZ::Transform::CreateFromQuaternionAndTranslation(AZ::Quaternion::CreateIdentity(), AZ::Vector3(100.0f, 100.0f, 0.0f)),
  242. AZ::Vector3(5.0f, 5.0f, 5.0f));
  243. bool rayHit = false;
  244. float distance;
  245. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  246. rayHit, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay,
  247. AZ::Vector3(100.0f, 100.0f, -100.0f), AZ::Vector3(0.0f, 0.0f, 1.0f), distance);
  248. EXPECT_TRUE(rayHit);
  249. EXPECT_NEAR(distance, 97.5f, 1e-2f);
  250. }
  251. // transformed scaled
  252. TEST_F(BoxShapeTest, GetRayIntersectBoxSuccess4)
  253. {
  254. AZ::Entity entity;
  255. CreateBox(
  256. entity,
  257. AZ::Transform::CreateFromQuaternionAndTranslation(
  258. AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisY(), AZ::Constants::QuarterPi), AZ::Vector3(0.0f, 0.0f, 5.0f)) *
  259. AZ::Transform::CreateUniformScale(3.0f),
  260. AZ::Vector3(2.0f, 4.0f, 1.0f));
  261. bool rayHit = false;
  262. float distance;
  263. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  264. rayHit, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay,
  265. AZ::Vector3(1.0f, -10.0f, 4.0f), AZ::Vector3(0.0f, 1.0f, 0.0f), distance);
  266. EXPECT_TRUE(rayHit);
  267. EXPECT_NEAR(distance, 4.0f, 1e-2f);
  268. }
  269. TEST_F(BoxShapeTest, GetRayIntersectBoxFailure)
  270. {
  271. AZ::Entity entity;
  272. CreateBox(
  273. entity,
  274. AZ::Transform::CreateFromQuaternionAndTranslation(AZ::Quaternion::CreateIdentity(), AZ::Vector3(0.0f, -10.0f, 0.0f)),
  275. AZ::Vector3(2.0f, 6.0f, 4.0f));
  276. bool rayHit = false;
  277. float distance;
  278. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  279. rayHit, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay,
  280. AZ::Vector3::CreateZero(), AZ::Vector3(1.0f, 0.0f, 0.0f), distance);
  281. EXPECT_FALSE(rayHit);
  282. }
  283. TEST_F(BoxShapeTest, GetRayIntersectBoxUnrotatedNonUniformScale)
  284. {
  285. AZ::Entity entity;
  286. AZ::Transform transform = AZ::Transform::CreateTranslation(AZ::Vector3(2.0f, -5.0f, 3.0f));
  287. transform.MultiplyByUniformScale(0.5f);
  288. const AZ::Vector3 dimensions(2.2f, 1.8f, 0.4f);
  289. const AZ::Vector3 nonUniformScale(0.2f, 2.6f, 1.2f);
  290. CreateBoxWithNonUniformScale(entity, transform, dimensions, nonUniformScale);
  291. // should just miss the box
  292. bool rayHit = false;
  293. float distance = AZ::Constants::FloatMax;
  294. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  295. rayHit, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay,
  296. AZ::Vector3(1.8f, -6.2f, 3.0f), AZ::Vector3(1.0f, 0.0f, 0.0f), distance);
  297. EXPECT_FALSE(rayHit);
  298. // should just hit the box
  299. rayHit = false;
  300. distance = AZ::Constants::FloatMax;
  301. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  302. rayHit, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay,
  303. AZ::Vector3(1.8f, -6.1f, 3.0f), AZ::Vector3(1.0f, 0.0f, 0.0f), distance);
  304. EXPECT_TRUE(rayHit);
  305. EXPECT_NEAR(distance, 0.09f, 1e-3f);
  306. // should just miss the box
  307. rayHit = false;
  308. distance = AZ::Constants::FloatMax;
  309. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  310. rayHit, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay,
  311. AZ::Vector3(2.2f, -6.2f, 3.0f), AZ::Vector3(0.0f, 1.0f, 0.0f), distance);
  312. EXPECT_FALSE(rayHit);
  313. // should just hit the box
  314. rayHit = false;
  315. distance = AZ::Constants::FloatMax;
  316. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  317. rayHit, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay,
  318. AZ::Vector3(2.1f, -6.2f, 3.0f), AZ::Vector3(0.0f, 1.0f, 0.0f), distance);
  319. EXPECT_TRUE(rayHit);
  320. EXPECT_NEAR(distance, 0.03f, 1e-3f);
  321. }
  322. TEST_F(BoxShapeTest, GetRayIntersectBoxRotatedNonUniformScale)
  323. {
  324. AZ::Entity entity;
  325. AZ::Transform transform = AZ::Transform::CreateFromQuaternionAndTranslation(
  326. AZ::Quaternion(0.50f, 0.10f, 0.02f, 0.86f), AZ::Vector3(4.0f, 1.0f, -2.0f));
  327. transform.MultiplyByUniformScale(1.5f);
  328. const AZ::Vector3 dimensions(1.2f, 0.7f, 2.1f);
  329. const AZ::Vector3 nonUniformScale(0.8f, 0.6f, 0.7f);
  330. CreateBoxWithNonUniformScale(entity, transform, dimensions, nonUniformScale);
  331. // should just miss the box
  332. bool rayHit = false;
  333. float distance = AZ::Constants::FloatMax;
  334. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  335. rayHit, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay,
  336. AZ::Vector3(5.0f, 0.6f, -1.5f), AZ::Vector3(-0.1f, 0.1f, -0.02f).GetNormalized(), distance);
  337. EXPECT_FALSE(rayHit);
  338. // should just hit the box
  339. rayHit = false;
  340. distance = AZ::Constants::FloatMax;
  341. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  342. rayHit, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay,
  343. AZ::Vector3(4.9f, 0.6f, -1.5f), AZ::Vector3(-0.1f, 0.1f, -0.02f).GetNormalized(), distance);
  344. EXPECT_TRUE(rayHit);
  345. EXPECT_NEAR(distance, 0.0553f, 1e-3f);
  346. }
  347. TEST_F(BoxShapeTest, GetAabbIdentityTransform)
  348. {
  349. // not rotated - AABB input
  350. AZ::Entity entity;
  351. CreateBox(entity, AZ::Transform::CreateIdentity(), AZ::Vector3(1.5f, 3.5f, 5.5f));
  352. AZ::Aabb aabb;
  353. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  354. aabb, entity.GetId(), &LmbrCentral::ShapeComponentRequests::GetEncompassingAabb);
  355. EXPECT_THAT(aabb.GetMin(), IsClose(AZ::Vector3(-0.75f, -1.75f, -2.75f)));
  356. EXPECT_THAT(aabb.GetMax(), IsClose(AZ::Vector3(0.75f, 1.75f, 2.75f)));
  357. }
  358. TEST_F(BoxShapeTest, GetAabbRotatedAndTranslated)
  359. {
  360. // rotated - OBB input
  361. AZ::Entity entity;
  362. CreateDefaultBox(
  363. AZ::Transform::CreateFromQuaternionAndTranslation(
  364. AZ::Quaternion::CreateRotationY(AZ::Constants::QuarterPi),
  365. AZ::Vector3(5.0f, 5.0f, 5.0f)), entity);
  366. AZ::Aabb aabb;
  367. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  368. aabb, entity.GetId(), &LmbrCentral::ShapeComponentRequestsBus::Events::GetEncompassingAabb);
  369. EXPECT_THAT(aabb.GetMin(), IsClose(AZ::Vector3(-2.07106f, 0.0f, -2.07106f)));
  370. EXPECT_THAT(aabb.GetMax(), IsClose(AZ::Vector3(12.07106f, 10.0f, 12.07106f)));
  371. }
  372. TEST_F(BoxShapeTest, GetAabbRotated)
  373. {
  374. // rotated - OBB input
  375. AZ::Entity entity;
  376. CreateBox(
  377. entity,
  378. AZ::Transform::CreateFromQuaternionAndTranslation(
  379. AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::QuarterPi) *
  380. AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisY(), AZ::Constants::QuarterPi),
  381. AZ::Vector3(0.0f, 0.0f, 0.0f)),
  382. AZ::Vector3(2.0f, 5.0f, 1.0f));
  383. AZ::Aabb aabb;
  384. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  385. aabb, entity.GetId(), &LmbrCentral::ShapeComponentRequests::GetEncompassingAabb);
  386. EXPECT_THAT(aabb.GetMin(), IsClose(AZ::Vector3(-1.06066f, -2.517766f, -2.517766f)));
  387. EXPECT_THAT(aabb.GetMax(), IsClose(AZ::Vector3(1.06066f, 2.517766f, 2.517766f)));
  388. }
  389. TEST_F(BoxShapeTest, GetAabbTranslated)
  390. {
  391. // not rotated - AABB input
  392. AZ::Entity entity;
  393. CreateBox(entity, AZ::Transform::CreateTranslation(AZ::Vector3(100.0f, 70.0f, 30.0f)), AZ::Vector3(1.8f, 3.5f, 5.2f));
  394. AZ::Aabb aabb;
  395. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  396. aabb, entity.GetId(), &LmbrCentral::ShapeComponentRequests::GetEncompassingAabb);
  397. EXPECT_THAT(aabb.GetMin(), IsClose(AZ::Vector3(99.1f, 68.25f, 27.4f)));
  398. EXPECT_THAT(aabb.GetMax(), IsClose(AZ::Vector3(100.9f, 71.75f, 32.6f)));
  399. }
  400. TEST_F(BoxShapeTest, GetAabbRotatedAndUniformScaled)
  401. {
  402. AZ::Entity entity;
  403. CreateBox(
  404. entity,
  405. AZ::Transform::CreateFromQuaternionAndTranslation(
  406. AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisY(), AZ::Constants::QuarterPi), AZ::Vector3::CreateZero()) *
  407. AZ::Transform::CreateUniformScale(3.0f),
  408. AZ::Vector3(2.0f, 4.0f, 1.0f));
  409. AZ::Aabb aabb;
  410. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  411. aabb, entity.GetId(), &LmbrCentral::ShapeComponentRequests::GetEncompassingAabb);
  412. EXPECT_THAT(aabb.GetMin(), IsClose(AZ::Vector3(-3.1819f, -6.0f, -3.1819f)));
  413. EXPECT_THAT(aabb.GetMax(), IsClose(AZ::Vector3(3.1819f, 6.0f, 3.1819f)));
  414. }
  415. TEST_F(BoxShapeTest, GetAabbRotatedAndNonUniformScaled)
  416. {
  417. AZ::Entity entity;
  418. const AZ::Transform transform = AZ::Transform::CreateFromQuaternionAndTranslation(
  419. AZ::Quaternion(0.08f, 0.44f, 0.16f, 0.88f), AZ::Vector3(1.0f, 2.0f, 3.0f));
  420. const AZ::Vector3 nonUniformScale(0.5f, 1.2f, 2.0f);
  421. const AZ::Vector3 boxDimensions(2.4f, 2.0f, 4.8f);
  422. CreateBoxWithNonUniformScale(entity, transform, nonUniformScale, boxDimensions);
  423. AZ::Aabb aabb;
  424. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  425. aabb, entity.GetId(), &LmbrCentral::ShapeComponentRequests::GetEncompassingAabb);
  426. EXPECT_THAT(aabb.GetMin(), IsClose(AZ::Vector3(-3.4304f, 0.6656f, -0.6672f)));
  427. EXPECT_THAT(aabb.GetMax(), IsClose(AZ::Vector3(5.4304f, 3.3344f, 6.6672f)));
  428. }
  429. TEST_F(BoxShapeTest, GetTransformAndLocalBounds1)
  430. {
  431. // not rotated - AABB input
  432. AZ::Entity entity;
  433. CreateBox(entity, AZ::Transform::CreateIdentity(), AZ::Vector3(1.5f, 3.5f, 5.5f));
  434. AZ::Transform transformOut;
  435. AZ::Aabb aabb;
  436. LmbrCentral::ShapeComponentRequestsBus::Event(entity.GetId(), &LmbrCentral::ShapeComponentRequests::GetTransformAndLocalBounds, transformOut, aabb);
  437. EXPECT_THAT(transformOut, IsClose(AZ::Transform::CreateIdentity()));
  438. EXPECT_THAT(aabb.GetMin(), IsClose(AZ::Vector3(-0.75f, -1.75f, -2.75f)));
  439. EXPECT_THAT(aabb.GetMax(), IsClose(AZ::Vector3(0.75f, 1.75f, 2.75f)));
  440. }
  441. TEST_F(BoxShapeTest, GetTransformAndLocalBounds2)
  442. {
  443. // not rotated - AABB input
  444. AZ::Entity entity;
  445. AZ::Transform transformIn = AZ::Transform::CreateFromQuaternionAndTranslation(
  446. AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::QuarterPi) * AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisY(), AZ::Constants::QuarterPi),
  447. AZ::Vector3(9.0f, 11.0f, 13.0f));
  448. transformIn.MultiplyByUniformScale(3.0f);
  449. CreateBox(entity, transformIn, AZ::Vector3(1.5f, 3.5f, 5.5f));
  450. AZ::Transform transformOut;
  451. AZ::Aabb aabb;
  452. LmbrCentral::ShapeComponentRequestsBus::Event(entity.GetId(), &LmbrCentral::ShapeComponentRequests::GetTransformAndLocalBounds, transformOut, aabb);
  453. EXPECT_THAT(transformOut, IsClose(transformIn));
  454. EXPECT_THAT(aabb.GetMin(), IsClose(AZ::Vector3(-0.75f, -1.75f, -2.75f)));
  455. EXPECT_THAT(aabb.GetMax(), IsClose(AZ::Vector3(0.75f, 1.75f, 2.75f)));
  456. }
  457. TEST_F(BoxShapeTest, GetTransformAndLocalBoundsNonUniformScale)
  458. {
  459. AZ::Entity entity;
  460. AZ::Transform transformIn = AZ::Transform::CreateFromQuaternionAndTranslation(
  461. AZ::Quaternion(0.62f, 0.62f, 0.14f, 0.46f), AZ::Vector3(0.8f, -1.2f, 2.7f));
  462. transformIn.MultiplyByUniformScale(2.0f);
  463. const AZ::Vector3 nonUniformScale(1.5f, 2.0f, 0.4f);
  464. const AZ::Vector3 boxDimensions(2.0f, 1.7f, 0.5f);
  465. CreateBoxWithNonUniformScale(entity, transformIn, nonUniformScale, boxDimensions);
  466. AZ::Transform transformOut;
  467. AZ::Aabb aabb;
  468. LmbrCentral::ShapeComponentRequestsBus::Event(entity.GetId(), &LmbrCentral::ShapeComponentRequests::GetTransformAndLocalBounds, transformOut, aabb);
  469. EXPECT_THAT(transformOut, IsClose(transformIn));
  470. // the local bounds should include the effect of non-uniform scale, but not the scale from the transform
  471. EXPECT_THAT(aabb.GetMin(), IsClose(AZ::Vector3(-1.5f, -1.7f, -0.1f)));
  472. EXPECT_THAT(aabb.GetMax(), IsClose(AZ::Vector3(1.5f, 1.7f, 0.1f)));
  473. }
  474. // point inside scaled
  475. TEST_F(BoxShapeTest, IsPointInside1)
  476. {
  477. AZ::Entity entity;
  478. CreateBox(
  479. entity,
  480. AZ::Transform::CreateFromQuaternionAndTranslation(
  481. AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisZ(), AZ::Constants::QuarterPi),
  482. AZ::Vector3(23.0f, 12.0f, 40.0f)) *
  483. AZ::Transform::CreateUniformScale(3.0f),
  484. AZ::Vector3(2.0f, 6.0f, 3.5f));
  485. // test some pairs of nearby points which should be just either side of the surface of the box
  486. EXPECT_TRUE(IsPointInside(entity, AZ::Vector3(28.0f, 5.0f, 36.0f)));
  487. EXPECT_FALSE(IsPointInside(entity, AZ::Vector3(29.0f, 5.0f, 36.0f)));
  488. EXPECT_TRUE(IsPointInside(entity, AZ::Vector3(24.0, 14.0f, 45.0f)));
  489. EXPECT_FALSE(IsPointInside(entity, AZ::Vector3(24.0, 14.0f, 46.0f)));
  490. EXPECT_TRUE(IsPointInside(entity, AZ::Vector3(16.0, 15.0f, 42.0f)));
  491. EXPECT_FALSE(IsPointInside(entity, AZ::Vector3(16.0, 14.0f, 42.0f)));
  492. }
  493. // point inside scaled
  494. TEST_F(BoxShapeTest, IsPointInside2)
  495. {
  496. AZ::Entity entity;
  497. CreateBox(
  498. entity,
  499. AZ::Transform::CreateTranslation(AZ::Vector3(23.0f, 12.0f, 40.0f)) * AZ::Transform::CreateRotationX(-AZ::Constants::QuarterPi) *
  500. AZ::Transform::CreateRotationZ(AZ::Constants::QuarterPi) * AZ::Transform::CreateUniformScale(2.0f),
  501. AZ::Vector3(4.0f, 7.0f, 3.5f));
  502. // test some pairs of nearby points which should be just either side of the surface of the box
  503. EXPECT_TRUE(IsPointInside(entity, AZ::Vector3(16.0f, 16.0f, 40.0f)));
  504. EXPECT_FALSE(IsPointInside(entity, AZ::Vector3(16.0f, 17.0f, 40.0f)));
  505. EXPECT_TRUE(IsPointInside(entity, AZ::Vector3(24.0f, 10.0f, 38.0f)));
  506. EXPECT_FALSE(IsPointInside(entity, AZ::Vector3(24.0f, 10.0f, 37.0f)));
  507. EXPECT_TRUE(IsPointInside(entity, AZ::Vector3(21.0f, 10.0f, 42.0f)));
  508. EXPECT_FALSE(IsPointInside(entity, AZ::Vector3(20.0f, 10.0f, 42.0f)));
  509. }
  510. TEST_F(BoxShapeTest, IsPointInsideNonUniformScale)
  511. {
  512. AZ::Entity entity;
  513. const AZ::Transform transform = AZ::Transform::CreateFromQuaternionAndTranslation(
  514. AZ::Quaternion(0.26f, 0.74f, 0.22f, 0.58f), AZ::Vector3(12.0f, -16.0f, 3.0f));
  515. const AZ::Vector3 nonUniformScale(0.5f, 2.0f, 3.0f);
  516. const AZ::Vector3 boxDimensions(4.0f, 3.0f, 7.0f);
  517. CreateBoxWithNonUniformScale(entity, transform, nonUniformScale, boxDimensions);
  518. // test some pairs of nearby points which should be just either side of the surface of the box
  519. EXPECT_TRUE(IsPointInside(entity, AZ::Vector3(2.0f, -16.0f, 6.0f)));
  520. EXPECT_FALSE(IsPointInside(entity, AZ::Vector3(1.0f, -16.0f, 6.0f)));
  521. EXPECT_TRUE(IsPointInside(entity, AZ::Vector3(13.0f, -14.0f, 5.0f)));
  522. EXPECT_FALSE(IsPointInside(entity, AZ::Vector3(13.0f, -13.0f, 5.0f)));
  523. EXPECT_TRUE(IsPointInside(entity, AZ::Vector3(9.0f, -18.0f, 3.0f)));
  524. EXPECT_FALSE(IsPointInside(entity, AZ::Vector3(9.0f, -18.0f, 4.0f)));
  525. }
  526. // distance scaled
  527. TEST_F(BoxShapeTest, DistanceFromPoint1)
  528. {
  529. AZ::Entity entity;
  530. CreateBox(
  531. entity,
  532. AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 37.0f, 32.0f)) * AZ::Transform::CreateRotationZ(AZ::Constants::QuarterPi) *
  533. AZ::Transform::CreateUniformScale(2.0f),
  534. AZ::Vector3(6.0f, 1.0f, 5.0f));
  535. float distance;
  536. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  537. distance, entity.GetId(), &LmbrCentral::ShapeComponentRequests::DistanceFromPoint, AZ::Vector3(4.0f, 33.5f, 38.0f));
  538. EXPECT_NEAR(distance, 1.45f, 1e-2f);
  539. }
  540. // distance scaled
  541. TEST_F(BoxShapeTest, DistanceFromPoint2)
  542. {
  543. AZ::Entity entity;
  544. CreateBox(
  545. entity,
  546. AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 37.0f, 32.0f)) * AZ::Transform::CreateRotationX(AZ::Constants::HalfPi) *
  547. AZ::Transform::CreateRotationY(AZ::Constants::HalfPi) * AZ::Transform::CreateUniformScale(0.5f),
  548. AZ::Vector3(24.0f, 4.0f, 20.0f));
  549. float distance;
  550. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  551. distance, entity.GetId(), &LmbrCentral::ShapeComponentRequests::DistanceFromPoint, AZ::Vector3(10.0f, 37.0f, 48.0f));
  552. EXPECT_NEAR(distance, 15.0f, 1e-2f);
  553. }
  554. TEST_F(BoxShapeTest, DistanceFromPointNonUniformScale)
  555. {
  556. AZ::Entity entity;
  557. AZ::Transform transform = AZ::Transform::CreateFromQuaternionAndTranslation(
  558. AZ::Quaternion::CreateRotationY(AZ::DegToRad(30.0f)), AZ::Vector3(3.0f, 4.0f, 5.0f));
  559. transform.MultiplyByUniformScale(2.0f);
  560. const AZ::Vector3 dimensions(2.0f, 3.0f, 1.5f);
  561. const AZ::Vector3 nonUniformScale(1.4f, 2.2f, 0.8f);
  562. CreateBoxWithNonUniformScale(entity, transform, nonUniformScale, dimensions);
  563. float distance = AZ::Constants::FloatMax;
  564. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  565. distance, entity.GetId(), &LmbrCentral::ShapeComponentRequests::DistanceFromPoint, AZ::Vector3(7.0f, 11.0f, 5.0f));
  566. EXPECT_NEAR(distance, 1.1140f, 1e-3f);
  567. }
  568. TEST_F(BoxShapeTest, DebugDraw)
  569. {
  570. AZ::Entity entity;
  571. AZ::Transform transform = AZ::Transform::CreateFromQuaternionAndTranslation(
  572. AZ::Quaternion(0.70f, 0.10f, 0.34f, 0.62f), AZ::Vector3(3.0f, -1.0f, 2.0f));
  573. transform.MultiplyByUniformScale(2.0f);
  574. const AZ::Vector3 dimensions(1.2f, 0.8f, 1.7f);
  575. const AZ::Vector3 nonUniformScale(2.4f, 1.3f, 1.8f);
  576. CreateBoxWithNonUniformScale(entity, transform, nonUniformScale, dimensions);
  577. UnitTest::TestDebugDisplayRequests testDebugDisplayRequests;
  578. AzFramework::EntityDebugDisplayEventBus::Event(entity.GetId(), &AzFramework::EntityDebugDisplayEvents::DisplayEntityViewport,
  579. AzFramework::ViewportInfo{ 0 }, testDebugDisplayRequests);
  580. const AZStd::vector<AZ::Vector3>& points = testDebugDisplayRequests.GetPoints();
  581. const AZ::Aabb debugDrawAabb = points.size() > 0 ? AZ::Aabb::CreatePoints(points.data(), points.size()) : AZ::Aabb::CreateNull();
  582. AZ::Aabb shapeAabb = AZ::Aabb::CreateNull();
  583. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  584. shapeAabb, entity.GetId(), &LmbrCentral::ShapeComponentRequests::GetEncompassingAabb);
  585. EXPECT_THAT(debugDrawAabb.GetMin(), IsClose(shapeAabb.GetMin()));
  586. EXPECT_THAT(debugDrawAabb.GetMax(), IsClose(shapeAabb.GetMax()));
  587. }
  588. TEST_F(BoxShapeTest, ShapeHasThreadsafeGetSetCalls)
  589. {
  590. // Verify that setting values from one thread and querying values from multiple other threads in parallel produces
  591. // correct, consistent results.
  592. // Create our box centered at 0 with our height and starting XY dimensions.
  593. AZ::Entity entity;
  594. CreateBox(
  595. entity,
  596. AZ::Transform::CreateTranslation(AZ::Vector3::CreateZero()),
  597. AZ::Vector3(ShapeThreadsafeTest::MinDimension, ShapeThreadsafeTest::MinDimension, ShapeThreadsafeTest::ShapeHeight));
  598. // Define the function for setting unimportant dimensions on the shape while queries take place.
  599. auto setDimensionFn = [](AZ::EntityId shapeEntityId, float minDimension, uint32_t dimensionVariance, float height)
  600. {
  601. float x = minDimension + aznumeric_cast<float>(rand() % dimensionVariance);
  602. float y = minDimension + aznumeric_cast<float>(rand() % dimensionVariance);
  603. LmbrCentral::BoxShapeComponentRequestsBus::Event(
  604. shapeEntityId, &LmbrCentral::BoxShapeComponentRequestsBus::Events::SetBoxDimensions, AZ::Vector3(x, y, height));
  605. };
  606. // Run the test, which will run multiple queries in parallel with each other and with the dimension-setting function.
  607. // The number of iterations is arbitrary - it's set high enough to catch most failures, but low enough to keep the test
  608. // time to a minimum.
  609. const int numIterations = 30000;
  610. ShapeThreadsafeTest::TestShapeGetSetCallsAreThreadsafe(entity, numIterations, setDimensionFn);
  611. }
  612. TEST_F(BoxShapeTest, UniformRealDistributionRandomPointsAreInAABBWithTranslationOffset)
  613. {
  614. AZ::Entity entity;
  615. AZ::Transform transform = AZ::Transform::CreateTranslation(AZ::Vector3(3.0f, -1.0f, -3.0f));
  616. transform.SetUniformScale(1.8f);
  617. const AZ::Vector3 dimensions(2.3f, 3.2f, 1.4f);
  618. const AZ::Vector3 nonUniformScale(0.2f, 0.5f, 0.2f);
  619. const AZ::Vector3 translationOffset(0.5f, 0.2f, 1.3f);
  620. CreateBoxWithNonUniformScale(entity, transform, nonUniformScale, dimensions, translationOffset);
  621. const bool allRandomPointsInVolume = RandomPointsAreInBox(entity, AZ::RandomDistributionType::UniformReal);
  622. EXPECT_TRUE(allRandomPointsInVolume);
  623. }
  624. TEST_F(BoxShapeTest, UniformRealDistributionRandomPointsAreInOBBWithTranslationOffset)
  625. {
  626. AZ::Entity entity;
  627. AZ::Transform transform =
  628. AZ::Transform::CreateFromQuaternionAndTranslation(AZ::Quaternion(0.34f, 0.50f, 0.38f, 0.70f), AZ::Vector3(-2.0f, 2.0f, 4.0f));
  629. transform.SetUniformScale(2.2f);
  630. const AZ::Vector3 dimensions(3.1f, 0.8f, 1.5f);
  631. const AZ::Vector3 nonUniformScale(0.4f, 0.1f, 0.3f);
  632. const AZ::Vector3 translationOffset(-3.5f, 2.2f, -1.8f);
  633. CreateBoxWithNonUniformScale(entity, transform, nonUniformScale, dimensions, translationOffset);
  634. const bool allRandomPointsInVolume = RandomPointsAreInBox(entity, AZ::RandomDistributionType::UniformReal);
  635. EXPECT_TRUE(allRandomPointsInVolume);
  636. }
  637. TEST_F(BoxShapeTest, GetRayIntersectBoxWithTranslationOffsetJustIntersecting)
  638. {
  639. AZ::Entity entity;
  640. AZ::Transform transform =
  641. AZ::Transform::CreateFromQuaternionAndTranslation(AZ::Quaternion(0.0f, 0.0f, 0.6f, 0.8f), AZ::Vector3(-2.0f, 2.0f, -4.0f));
  642. transform.SetUniformScale(3.0f);
  643. const AZ::Vector3 dimensions(3.0f, 4.0f, 5.0f);
  644. const AZ::Vector3 nonUniformScale(2.0f, 0.5f, 0.5f);
  645. const AZ::Vector3 translationOffset(1.0f, 2.0f, 3.0f);
  646. CreateBoxWithNonUniformScale(entity, transform, nonUniformScale, dimensions, translationOffset);
  647. bool rayHit = false;
  648. float distance;
  649. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  650. rayHit, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay,
  651. AZ::Vector3(-8.593f, 1.0f, 0.0f), AZ::Vector3(0.0f, -1.0f, 0.0f), distance);
  652. EXPECT_TRUE(rayHit);
  653. EXPECT_NEAR(distance, 0.176f, 1e-3f);
  654. }
  655. TEST_F(BoxShapeTest, GetRayIntersectBoxWithTranslationOffsetJustMissing)
  656. {
  657. AZ::Entity entity;
  658. AZ::Transform transform =
  659. AZ::Transform::CreateFromQuaternionAndTranslation(AZ::Quaternion(0.0f, 0.0f, 0.6f, 0.8f), AZ::Vector3(-2.0f, 2.0f, -4.0f));
  660. transform.SetUniformScale(3.0f);
  661. const AZ::Vector3 dimensions(3.0f, 4.0f, 5.0f);
  662. const AZ::Vector3 nonUniformScale(2.0f, 0.5f, 0.5f);
  663. const AZ::Vector3 translationOffset(1.0f, 2.0f, 3.0f);
  664. CreateBoxWithNonUniformScale(entity, transform, nonUniformScale, dimensions, translationOffset);
  665. bool rayHit = false;
  666. float distance;
  667. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  668. rayHit, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay,
  669. AZ::Vector3(-8.601f, 1.0f, 0.0f), AZ::Vector3(0.0f, -1.0f, 0.0f), distance);
  670. EXPECT_FALSE(rayHit);
  671. }
  672. TEST_F(BoxShapeTest, GetAabbRotatedAndScaledWithTranslationOffset)
  673. {
  674. AZ::Entity entity;
  675. AZ::Transform transform = AZ::Transform::CreateFromQuaternionAndTranslation(
  676. AZ::Quaternion(0.1f, 0.7f, 0.1f, 0.7f), AZ::Vector3(2.0f, 5.0f, -3.0f));
  677. transform.SetUniformScale(2.5f);
  678. const AZ::Vector3 nonUniformScale(0.8f, 2.2f, 0.5f);
  679. const AZ::Vector3 boxDimensions(3.2f, 1.6f, 4.8f);
  680. const AZ::Vector3 translationOffset(2.0f, 2.0f, 6.0f);
  681. CreateBoxWithNonUniformScale(entity, transform, nonUniformScale, boxDimensions, translationOffset);
  682. AZ::Aabb aabb;
  683. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  684. aabb, entity.GetId(), &LmbrCentral::ShapeComponentRequests::GetEncompassingAabb);
  685. EXPECT_THAT(aabb.GetMin(), IsClose(AZ::Vector3(6.5f, 11.56f, -8.064f)));
  686. EXPECT_THAT(aabb.GetMax(), IsClose(AZ::Vector3(12.5f, 21.8f, 0.544f)));
  687. }
  688. TEST_F(BoxShapeTest, GetAabbUnrotatedScaledWithTranslationOffset)
  689. {
  690. AZ::Entity entity;
  691. AZ::Transform transform = AZ::Transform::CreateTranslation(AZ::Vector3(3.0f, 2.0f, -5.0f));
  692. transform.SetUniformScale(1.5f);
  693. const AZ::Vector3 nonUniformScale(1.8f, 0.6f, 0.4f);
  694. const AZ::Vector3 boxDimensions(1.2f, 3.4f, 2.2f);
  695. const AZ::Vector3 translationOffset(-5.0f, -6.0f, 3.0f);
  696. CreateBoxWithNonUniformScale(entity, transform, nonUniformScale, boxDimensions, translationOffset);
  697. AZ::Aabb aabb;
  698. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  699. aabb, entity.GetId(), &LmbrCentral::ShapeComponentRequests::GetEncompassingAabb);
  700. EXPECT_THAT(aabb.GetMin(), IsClose(AZ::Vector3(-12.12f, -4.93f, -3.86f)));
  701. EXPECT_THAT(aabb.GetMax(), IsClose(AZ::Vector3(-8.88f, -1.87f, -2.54f)));
  702. }
  703. TEST_F(BoxShapeTest, GetTransformAndLocalBoundsWithTranslationOffset)
  704. {
  705. AZ::Entity entity;
  706. AZ::Transform transform =
  707. AZ::Transform::CreateFromQuaternionAndTranslation(AZ::Quaternion(0.46f, 0.26f, 0.58f, 0.62f), AZ::Vector3(3.0f, 2.0f, -5.0f));
  708. transform.SetUniformScale(0.7f);
  709. const AZ::Vector3 nonUniformScale(1.6f, 1.1f, 0.6f);
  710. const AZ::Vector3 boxDimensions(2.5f, 2.0f, 3.0f);
  711. const AZ::Vector3 translationOffset(-4.0f, 3.0f, -2.0f);
  712. CreateBoxWithNonUniformScale(entity, transform, nonUniformScale, boxDimensions, translationOffset);
  713. AZ::Transform transformOut;
  714. AZ::Aabb aabb;
  715. LmbrCentral::ShapeComponentRequestsBus::Event(entity.GetId(), &LmbrCentral::ShapeComponentRequests::GetTransformAndLocalBounds, transformOut, aabb);
  716. EXPECT_THAT(transformOut, IsClose(transform));
  717. EXPECT_THAT(aabb.GetMin(), IsClose(AZ::Vector3(-8.4f, 2.2f, -2.1f)));
  718. EXPECT_THAT(aabb.GetMax(), IsClose(AZ::Vector3(-4.4f, 4.4f, -0.3f)));
  719. }
  720. TEST_F(BoxShapeTest, IsPointInsideRotatedWithTranslationOffset)
  721. {
  722. AZ::Entity entity;
  723. AZ::Transform transform =
  724. AZ::Transform::CreateFromQuaternionAndTranslation(AZ::Quaternion(0.48f, 0.24f, 0.44f, 0.72f), AZ::Vector3(2.0f, -1.0f, 2.0f));
  725. transform.SetUniformScale(1.5f);
  726. const AZ::Vector3 nonUniformScale(1.2f, 0.8f, 3.6f);
  727. const AZ::Vector3 boxDimensions(4.0f, 2.5f, 1.0f);
  728. const AZ::Vector3 translationOffset(3.0f, 5.0f, -1.0f);
  729. CreateBoxWithNonUniformScale(entity, transform, nonUniformScale, boxDimensions, translationOffset);
  730. // test some pairs of nearby points which should be just either side of the surface of the box
  731. EXPECT_TRUE(IsPointInside(entity, AZ::Vector3(-0.15f, 10.4f, 4.66f)));
  732. EXPECT_FALSE(IsPointInside(entity, AZ::Vector3(-0.15f, 10.42f, 4.66f)));
  733. EXPECT_TRUE(IsPointInside(entity, AZ::Vector3(-0.17f, 8.13f, 4.49f)));
  734. EXPECT_FALSE(IsPointInside(entity, AZ::Vector3(-0.17f, 8.13f, 4.47f)));
  735. EXPECT_TRUE(IsPointInside(entity, AZ::Vector3(-6.34f, 5.58f, 5.47f)));
  736. EXPECT_FALSE(IsPointInside(entity, AZ::Vector3(-6.36f, 5.58f, 5.47f)));
  737. }
  738. TEST_F(BoxShapeTest, IsPointInsideUnrotatedWithTranslationOffset)
  739. {
  740. AZ::Entity entity;
  741. AZ::Transform transform =
  742. AZ::Transform::CreateTranslation(AZ::Vector3(4.0f, 4.0f, -3.0f));
  743. transform.SetUniformScale(1.5f);
  744. const AZ::Vector3 nonUniformScale(0.8f, 0.6f, 1.8f);
  745. const AZ::Vector3 boxDimensions(1.5f, 4.0f, 2.0f);
  746. const AZ::Vector3 translationOffset(5.0f, -1.0f, 3.0f);
  747. CreateBoxWithNonUniformScale(entity, transform, nonUniformScale, boxDimensions, translationOffset);
  748. // test some pairs of nearby points which should be just either side of the surface of the box
  749. EXPECT_TRUE(IsPointInside(entity, AZ::Vector3(9.11f, 3.0f, 5.0f)));
  750. EXPECT_FALSE(IsPointInside(entity, AZ::Vector3(9.09f, 3.0f, 5.0f)));
  751. EXPECT_TRUE(IsPointInside(entity, AZ::Vector3(10.0f, 4.89f, 6.0f)));
  752. EXPECT_FALSE(IsPointInside(entity, AZ::Vector3(10.0f, 4.91f, 6.0f)));
  753. EXPECT_TRUE(IsPointInside(entity, AZ::Vector3(10.89f, 1.31f, 2.41f)));
  754. EXPECT_FALSE(IsPointInside(entity, AZ::Vector3(10.91f, 1.29f, 2.39f)));
  755. }
  756. TEST_F(BoxShapeTest, DistanceFromPointRotatedWithTranslationOffset)
  757. {
  758. AZ::Entity entity;
  759. AZ::Transform transform = AZ::Transform::CreateFromQuaternionAndTranslation(
  760. AZ::Quaternion(0.40f, 0.20f, 0.40f, 0.80f), AZ::Vector3(-4.0f, -4.0f, 7.0f));
  761. transform.SetUniformScale(1.5f);
  762. const AZ::Vector3 dimensions(2.4f, 3.0f, 0.6f);
  763. const AZ::Vector3 nonUniformScale(2.0f, 1.5f, 4.0f);
  764. const AZ::Vector3 translationOffset(2.0f, 2.0f, -3.0f);
  765. CreateBoxWithNonUniformScale(entity, transform, nonUniformScale, dimensions, translationOffset);
  766. float distance = AZ::Constants::FloatMax;
  767. // should be inside
  768. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  769. distance, entity.GetId(), &LmbrCentral::ShapeComponentRequests::DistanceFromPoint, AZ::Vector3(-14.8f, 11.6f, 1.0f));
  770. EXPECT_NEAR(distance, 0.0f, 1e-3f);
  771. // should be closest to a face
  772. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  773. distance, entity.GetId(), &LmbrCentral::ShapeComponentRequests::DistanceFromPoint, AZ::Vector3(-17.2f, 8.4f, 1.0f));
  774. EXPECT_NEAR(distance, 0.4f, 1e-3f);
  775. // should be closest to an edge
  776. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  777. distance, entity.GetId(), &LmbrCentral::ShapeComponentRequests::DistanceFromPoint, AZ::Vector3(-13.444f, 15.583f, 2.74f));
  778. EXPECT_NEAR(distance, 0.5f, 1e-3f);
  779. // should be closest to a corner
  780. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  781. distance, entity.GetId(), &LmbrCentral::ShapeComponentRequests::DistanceFromPoint, AZ::Vector3(-20.02f, 10.515f, 2.2f));
  782. EXPECT_NEAR(distance, 1.3f, 1e-3f);
  783. }
  784. TEST_F(BoxShapeTest, DistanceFromPointUnrotatedWithTranslationOffset)
  785. {
  786. AZ::Entity entity;
  787. AZ::Transform transform = AZ::Transform::CreateTranslation(AZ::Vector3(-2.0f, 5.0f, -4.0f));
  788. transform.SetUniformScale(1.8f);
  789. const AZ::Vector3 dimensions(2.5f, 2.0f, 4.0f);
  790. const AZ::Vector3 nonUniformScale(4.0f, 2.0f, 0.5f);
  791. const AZ::Vector3 translationOffset(-5.0f, -2.0f, -1.0f);
  792. CreateBoxWithNonUniformScale(entity, transform, nonUniformScale, dimensions, translationOffset);
  793. float distance = AZ::Constants::FloatMax;
  794. // should be inside
  795. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  796. distance, entity.GetId(), &LmbrCentral::ShapeComponentRequests::DistanceFromPoint, AZ::Vector3(-40.0f, 0.0f, -4.0f));
  797. EXPECT_NEAR(distance, 0.0f, 1e-3f);
  798. // should be closest to a face
  799. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  800. distance, entity.GetId(), &LmbrCentral::ShapeComponentRequests::DistanceFromPoint, AZ::Vector3(-40.0f, 0.0f, -3.0f));
  801. EXPECT_NEAR(distance, 0.1f, 1e-3f);
  802. // should be closest to an edge
  803. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  804. distance, entity.GetId(), &LmbrCentral::ShapeComponentRequests::DistanceFromPoint, AZ::Vector3(-40.0f, 2.0f, -7.5f));
  805. EXPECT_NEAR(distance, 1.0f, 1e-3f);
  806. // should be closest to a corner
  807. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  808. distance, entity.GetId(), &LmbrCentral::ShapeComponentRequests::DistanceFromPoint, AZ::Vector3(-26.6f, 2.0f, -2.3f));
  809. EXPECT_NEAR(distance, 2.6f, 1e-3f);
  810. }
  811. TEST_F(BoxShapeTest, DebugDrawWithTranslationOffset)
  812. {
  813. AZ::Entity entity;
  814. AZ::Transform transform = AZ::Transform::CreateFromQuaternionAndTranslation(
  815. AZ::Quaternion(0.1f, 0.1f, 0.7f, 0.7f), AZ::Vector3(1.0f, 6.0f, -3.0f));
  816. transform.SetUniformScale(1.2f);
  817. const AZ::Vector3 dimensions(3.6f, 2.0f, 1.6f);
  818. const AZ::Vector3 nonUniformScale(2.5f, 1.0f, 5.0f);
  819. const AZ::Vector3 translationOffset(-4.0f, -3.0f, 5.0f);
  820. CreateBoxWithNonUniformScale(entity, transform, nonUniformScale, dimensions, translationOffset);
  821. UnitTest::TestDebugDisplayRequests testDebugDisplayRequests;
  822. AzFramework::EntityDebugDisplayEventBus::Event(entity.GetId(), &AzFramework::EntityDebugDisplayEvents::DisplayEntityViewport,
  823. AzFramework::ViewportInfo{ 0 }, testDebugDisplayRequests);
  824. const AZStd::vector<AZ::Vector3>& points = testDebugDisplayRequests.GetPoints();
  825. const AZ::Aabb debugDrawAabb = points.size() > 0 ? AZ::Aabb::CreatePoints(points.data(), points.size()) : AZ::Aabb::CreateNull();
  826. EXPECT_THAT(debugDrawAabb.GetMin(), IsClose(AZ::Vector3(10.36f, -11.4f, 19.848f)));
  827. EXPECT_THAT(debugDrawAabb.GetMax(), IsClose(AZ::Vector3(15.352f, -0.6f, 29.736f)));
  828. }
  829. TEST_F(BoxShapeTest, IsTypeAxisAlignedReturnsFalse)
  830. {
  831. AZ::Entity entity;
  832. CreateDefaultBox(AZ::Transform::CreateIdentity(), entity);
  833. bool isTypeAxisAligned = true;
  834. LmbrCentral::BoxShapeComponentRequestsBus::EventResult(
  835. isTypeAxisAligned, entity.GetId(), &LmbrCentral::BoxShapeComponentRequests::IsTypeAxisAligned);
  836. EXPECT_FALSE(isTypeAxisAligned);
  837. }
  838. }