WhiteBoxComponentTest.cpp 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882
  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 "Asset/EditorWhiteBoxMeshAsset.h"
  9. #include "EditorWhiteBoxComponentMode.h"
  10. #include "EditorWhiteBoxPolygonModifierBus.h"
  11. #include "EditorWhiteBoxSystemComponent.h"
  12. #include "Rendering/WhiteBoxNullRenderMesh.h"
  13. #include "SubComponentModes/EditorWhiteBoxDefaultModeBus.h"
  14. #include "Viewport/WhiteBoxManipulatorViews.h"
  15. #include "WhiteBox/EditorWhiteBoxComponentBus.h"
  16. #include "WhiteBoxTestFixtures.h"
  17. #include "WhiteBoxTestUtil.h"
  18. #include <AZTestShared/Math/MathTestHelpers.h>
  19. #include <AzCore/Asset/AssetManager.h>
  20. #include <AzCore/IO/Path/Path.h>
  21. #include <AzCore/Math/Matrix3x3.h>
  22. #include <AzFramework/Entity/EntityDebugDisplayBus.h>
  23. #include <AzFramework/UnitTest/TestDebugDisplayRequests.h>
  24. #include <AzFramework/Viewport/CameraState.h>
  25. #include <AzManipulatorTestFramework/AzManipulatorTestFramework.h>
  26. #include <AzManipulatorTestFramework/AzManipulatorTestFrameworkUtils.h>
  27. #include <AzManipulatorTestFramework/DirectManipulatorViewportInteraction.h>
  28. #include <AzManipulatorTestFramework/ImmediateModeActionDispatcher.h>
  29. #include <AzToolsFramework/API/ToolsApplicationAPI.h>
  30. #include <AzToolsFramework/Entity/EditorEntityHelpers.h>
  31. #include <AzToolsFramework/Manipulators/LinearManipulator.h>
  32. #include <AzToolsFramework/Manipulators/ManipulatorManager.h>
  33. #include <QMessageBox>
  34. namespace UnitTest
  35. {
  36. static const AzToolsFramework::ManipulatorManagerId TestManipulatorManagerId =
  37. AzToolsFramework::ManipulatorManagerId(AZ::Crc32("TestManipulatorManagerId"));
  38. class WhiteBoxManipulatorFixture : public WhiteBoxTestFixture
  39. {
  40. public:
  41. void SetUpEditorFixtureImpl() override;
  42. void TearDownEditorFixtureImpl() override;
  43. AZStd::unique_ptr<AzToolsFramework::ManipulatorManager> m_manipulatorManager;
  44. };
  45. void WhiteBoxManipulatorFixture::SetUpEditorFixtureImpl()
  46. {
  47. m_manipulatorManager = AZStd::make_unique<AzToolsFramework::ManipulatorManager>(TestManipulatorManagerId);
  48. }
  49. void WhiteBoxManipulatorFixture::TearDownEditorFixtureImpl()
  50. {
  51. m_manipulatorManager.reset();
  52. }
  53. TEST_F(WhiteBoxManipulatorFixture, ManipulatorBoundsRefreshedAfterBeingMarkedDirty)
  54. {
  55. namespace Api = WhiteBox::Api;
  56. // create the direct call manipulator viewport interaction and an immediate mode dispatcher
  57. AZStd::unique_ptr<AzManipulatorTestFramework::ManipulatorViewportInteraction> viewportManipulatorInteraction =
  58. AZStd::make_unique<AzManipulatorTestFramework::DirectCallManipulatorViewportInteraction>(
  59. AZStd::make_shared<NullDebugDisplayRequests>());
  60. AZStd::unique_ptr<AzManipulatorTestFramework::ImmediateModeActionDispatcher> actionDispatcher =
  61. AZStd::make_unique<AzManipulatorTestFramework::ImmediateModeActionDispatcher>(
  62. *viewportManipulatorInteraction);
  63. // create and register the manipulator with the test manipulator manager
  64. auto manipulator = AzToolsFramework::LinearManipulator::MakeShared(AZ::Transform::CreateIdentity());
  65. manipulator->Register(viewportManipulatorInteraction->GetManipulatorManagerId());
  66. // create a simple white box mesh
  67. Api::InitializeAsUnitQuad(*m_whiteBox);
  68. // create polygon manipulator view from white box
  69. const Api::PolygonHandle polygonHandle = Api::FacePolygonHandle(*m_whiteBox, Api::FaceHandle{0});
  70. const Api::VertexPositionsCollection outlines = Api::PolygonBorderVertexPositions(*m_whiteBox, polygonHandle);
  71. const AZStd::vector<AZ::Vector3> triangles = Api::PolygonFacesPositions(*m_whiteBox, polygonHandle);
  72. auto polygonView = WhiteBox::CreateManipulatorViewPolygon(triangles, outlines);
  73. AzToolsFramework::ManipulatorViews views;
  74. views.emplace_back(AZStd::move(polygonView));
  75. manipulator->SetViews(views);
  76. // position the manipulator offset down the y axis
  77. const AZ::Vector3& initialPosition = AZ::Vector3::CreateAxisY(10.0f);
  78. manipulator->SetLocalPosition(initialPosition);
  79. // simple callback to update the manipulator's current position
  80. manipulator->InstallMouseMoveCallback(
  81. [initialPosition, &manipulator](const AzToolsFramework::LinearManipulator::Action& action)
  82. {
  83. manipulator->SetLocalPosition(initialPosition + action.LocalPositionOffset());
  84. });
  85. // camera state to represent the viewer in world space
  86. auto cameraState = AzFramework::CreateIdentityDefaultCamera(AZ::Vector3::CreateZero(), DefaultViewportSize);
  87. // the initial starting position of the mouse (center of viewport)
  88. const auto initialPositionScreen = AzManipulatorTestFramework::GetCameraStateViewportCenter(cameraState);
  89. // the final position of the mouse (an arbitrary amount of pixels to the right)
  90. const auto finalPositionScreen =
  91. AzFramework::ScreenPoint(initialPositionScreen.m_x + 100, initialPositionScreen.m_y);
  92. actionDispatcher->CameraState(cameraState)
  93. ->Trace("center the camera at the origin")
  94. ->CameraState(AzManipulatorTestFramework::SetCameraStatePosition(AZ::Vector3::CreateZero(), cameraState))
  95. ->Trace("point the camera down the y axis")
  96. ->CameraState(AzManipulatorTestFramework::SetCameraStateDirection(AZ::Vector3::CreateAxisY(), cameraState))
  97. ->Trace("move to a valid position so the mouse pick ray intersects the manipulator bound (view)")
  98. ->MousePosition(initialPositionScreen)
  99. ->Trace("verify precondition - the manipulator recognizes is has the mouse over it")
  100. ->ExpectTrue(manipulator->MouseOver())
  101. ->Trace("simulate a click and drag motion (click and then move the camera to the right)")
  102. ->MouseLButtonDown()
  103. ->MousePosition(finalPositionScreen)
  104. ->Trace("mouse up after (ending drag)")
  105. ->MouseLButtonUp()
  106. ->Trace("simulate event from Qt (immediate mouse move after mouse up)")
  107. ->MousePosition(finalPositionScreen)
  108. ->ExpectTrue(manipulator->MouseOver());
  109. ;
  110. }
  111. TEST_F(EditorWhiteBoxComponentTestFixture, EditorWhiteBoxComponentRespectsEntityHiddenVisibility)
  112. {
  113. // given (precondition)
  114. EXPECT_TRUE(m_whiteBoxComponent->HasRenderMesh());
  115. // when
  116. AzToolsFramework::SetEntityVisibility(m_whiteBoxEntityId, false);
  117. // then
  118. EXPECT_FALSE(m_whiteBoxComponent->HasRenderMesh());
  119. }
  120. TEST_F(EditorWhiteBoxComponentTestFixture, EditorWhiteBoxComponentRespectsEntityHiddenVisibilityWhenActivated)
  121. {
  122. // given (precondition)
  123. EXPECT_TRUE(m_whiteBoxComponent->HasRenderMesh());
  124. m_whiteBoxComponent->Deactivate();
  125. // when
  126. AzToolsFramework::SetEntityVisibility(m_whiteBoxEntityId, false);
  127. // then
  128. m_whiteBoxComponent->Activate();
  129. EXPECT_FALSE(m_whiteBoxComponent->HasRenderMesh());
  130. }
  131. // simple listener class to listen for changes to polygon handles after a change
  132. // by a polygon modifier
  133. class PolygonModifierDetector : private WhiteBox::EditorWhiteBoxPolygonModifierNotificationBus::Handler
  134. {
  135. void OnPolygonModifierUpdatedPolygonHandle(
  136. const WhiteBox::Api::PolygonHandle& previousPolygonHandle,
  137. const WhiteBox::Api::PolygonHandle& nextPolygonHandle) override
  138. {
  139. m_previousPolygonHandle = previousPolygonHandle;
  140. m_nextPolygonHandle = nextPolygonHandle;
  141. }
  142. public:
  143. PolygonModifierDetector(const AZ::EntityComponentIdPair& entityComponentIdPair)
  144. {
  145. WhiteBox::EditorWhiteBoxPolygonModifierNotificationBus::Handler::BusConnect(entityComponentIdPair);
  146. }
  147. ~PolygonModifierDetector()
  148. {
  149. WhiteBox::EditorWhiteBoxPolygonModifierNotificationBus::Handler::BusDisconnect();
  150. }
  151. WhiteBox::Api::PolygonHandle m_previousPolygonHandle;
  152. WhiteBox::Api::PolygonHandle m_nextPolygonHandle;
  153. };
  154. TEST_F(EditorWhiteBoxModifierTestFixture, SelectedPolygonHandleModifierUpdatesAfterExtrusion)
  155. {
  156. namespace Api = WhiteBox::Api;
  157. using AzToolsFramework::ViewportInteraction::KeyboardModifier;
  158. using ::testing::Ne;
  159. // the initial starting position of the entity (in front just below the camera)
  160. const AZ::Transform initialEntityTransformWorld =
  161. AZ::Transform::CreateTranslation(AZ::Vector3(0.0f, 8.0f, 23.0f));
  162. const auto entityComponentIdPair = AZ::EntityComponentIdPair(m_whiteBoxEntityId, m_whiteBoxComponent->GetId());
  163. // grab the White Box Mesh (for use with the White Box Tool Api)
  164. WhiteBox::WhiteBoxMesh* whiteBox = nullptr;
  165. WhiteBox::EditorWhiteBoxComponentRequestBus::EventResult(
  166. whiteBox, entityComponentIdPair, &WhiteBox::EditorWhiteBoxComponentRequestBus::Events::GetWhiteBoxMesh);
  167. // create a 3x3 grid from the starting cube and hide all top edges
  168. Initialize3x3CubeGrid(*whiteBox);
  169. HideAllTopUserEdgesFor3x3Grid(*whiteBox);
  170. // move the entity to its starting position
  171. AzToolsFramework::SetWorldTransform(m_whiteBoxEntityId, initialEntityTransformWorld);
  172. // select the entity with the White Box Component
  173. AzToolsFramework::SelectEntity(m_whiteBoxEntityId);
  174. // mimic pressing the 'Edit' button on the Component Card
  175. EnterComponentMode<WhiteBox::EditorWhiteBoxComponent>();
  176. // used to listen for when a polygon handle has been updated (the same as in DefaultMode)
  177. PolygonModifierDetector polygonModifierDetector(entityComponentIdPair);
  178. AzFramework::SetCameraTransform(
  179. m_cameraState, AZ::Transform::CreateTranslation(AZ::Vector3(0.0f, 0.0f, 25.0f)));
  180. // the middle of the top, merged polygon (3x3)
  181. MultiSpacePoint topPolygonMidpoint(
  182. Api::PolygonMidpoint(*whiteBox, Api::FacePolygonHandle(*whiteBox, Api::FaceHandle(36))),
  183. initialEntityTransformWorld, m_cameraState);
  184. // middle of simple square polygon at the bottom right of the screen facing the camera
  185. MultiSpacePoint forwardPolygonMidpoint(
  186. Api::PolygonMidpoint(*whiteBox, Api::FacePolygonHandle(*whiteBox, Api::FaceHandle(18))),
  187. initialEntityTransformWorld, m_cameraState);
  188. // the position to move to when doing the polygon impression
  189. MultiSpacePoint movedForwardPolygonMidpoint(
  190. forwardPolygonMidpoint.GetLocalSpace() + AZ::Vector3::CreateAxisY(), initialEntityTransformWorld,
  191. m_cameraState);
  192. m_actionDispatcher
  193. ->CameraState(m_cameraState)
  194. // move the mouse to the top middle of the merged polygons (3x3 square grid)
  195. ->MousePosition(topPolygonMidpoint.GetScreenSpace())
  196. // select the polygon - creates a scale manipulator
  197. ->MouseLButtonDown()
  198. ->MouseLButtonUp()
  199. // move the mouse to the front right polygon facing the camera
  200. ->MousePosition(forwardPolygonMidpoint.GetScreenSpace())
  201. // appends inwards creating an impression by 1 meter in the y axis
  202. ->KeyboardModifierDown(KeyboardModifier::Ctrl)
  203. ->MouseLButtonDown()
  204. ->MousePosition(movedForwardPolygonMidpoint.GetScreenSpace())
  205. // release after moving the polygon manipulator
  206. ->MouseLButtonUp();
  207. // the size of the polygon handles should be the same (no faces have been merged or split)
  208. EXPECT_EQ(
  209. polygonModifierDetector.m_nextPolygonHandle.m_faceHandles.size(),
  210. polygonModifierDetector.m_previousPolygonHandle.m_faceHandles.size());
  211. // but the handles will have changed as the mesh will have updates after internally adding/removing new verts
  212. EXPECT_THAT(polygonModifierDetector.m_nextPolygonHandle, Ne(polygonModifierDetector.m_previousPolygonHandle));
  213. }
  214. TEST_F(EditorWhiteBoxModifierTestFixture, SwitchToRestoreModeDestroysModifierWhileInteractingWithFace)
  215. {
  216. namespace Api = WhiteBox::Api;
  217. using AzToolsFramework::ViewportInteraction::KeyboardModifier;
  218. const auto entityComponentIdPair = AZ::EntityComponentIdPair(m_whiteBoxEntityId, m_whiteBoxComponent->GetId());
  219. const auto displayEntityViewport = [whiteBoxEntityId = m_whiteBoxEntityId]()
  220. {
  221. AzFramework::EntityDebugDisplayEventBus::Event(
  222. whiteBoxEntityId, &AzFramework::EntityDebugDisplayEvents::DisplayEntityViewport,
  223. AzFramework::ViewportInfo{0}, NullDebugDisplayRequests{});
  224. };
  225. // helper to check which submode we're in
  226. const auto subMode = [entityComponentIdPair]()
  227. {
  228. WhiteBox::SubMode subMode;
  229. WhiteBox::EditorWhiteBoxComponentModeRequestBus::EventResult(
  230. subMode, entityComponentIdPair,
  231. &WhiteBox::EditorWhiteBoxComponentModeRequestBus::Events::GetCurrentSubMode);
  232. return subMode;
  233. };
  234. // the initial starting position of the entity (in front of and just below the camera)
  235. const AZ::Transform initialEntityTransformWorld =
  236. AZ::Transform::CreateTranslation(AZ::Vector3(0.0f, 7.0f, 23.0f));
  237. // grab the White Box Mesh (for use with the White Box Tool Api)
  238. WhiteBox::WhiteBoxMesh* whiteBox = nullptr;
  239. WhiteBox::EditorWhiteBoxComponentRequestBus::EventResult(
  240. whiteBox, entityComponentIdPair, &WhiteBox::EditorWhiteBoxComponentRequestBus::Events::GetWhiteBoxMesh);
  241. // move the entity to its starting position
  242. AzToolsFramework::SetWorldTransform(m_whiteBoxEntityId, initialEntityTransformWorld);
  243. // select the entity with the White Box Component
  244. AzToolsFramework::SelectEntity(m_whiteBoxEntityId);
  245. // mimic pressing the 'Edit' button on the Component Card
  246. EnterComponentMode<WhiteBox::EditorWhiteBoxComponent>();
  247. // override the default modifier key behavior for the white box component mode
  248. WhiteBox::EditorWhiteBoxComponentModeRequestBus::Event(
  249. entityComponentIdPair,
  250. &WhiteBox::EditorWhiteBoxComponentModeRequestBus::Events::OverrideKeyboardModifierQuery,
  251. [this]()
  252. {
  253. return m_actionDispatcher->QueryKeyboardModifiers();
  254. });
  255. AzFramework::SetCameraTransform(
  256. m_cameraState,
  257. AZ::Transform::CreateFromMatrix3x3AndTranslation(
  258. AZ::Matrix3x3::CreateRotationX(AZ::DegToRad(-45.0f)), AZ::Vector3(0.0f, 4.0f, 26.0f)));
  259. const AZ::Vector3 topPolygonMidpointLocal =
  260. Api::PolygonMidpoint(*whiteBox, Api::FacePolygonHandle(*whiteBox, Api::FaceHandle(1)));
  261. const AZ::Vector3 topPolygonNextPositionLocal = topPolygonMidpointLocal + AZ::Vector3::CreateAxisZ(0.5f);
  262. // the middle of the top
  263. MultiSpacePoint topPolygonMidpoint(topPolygonMidpointLocal, initialEntityTransformWorld, m_cameraState);
  264. MultiSpacePoint topPolygonNext(topPolygonNextPositionLocal, initialEntityTransformWorld, m_cameraState);
  265. // begin interacting with a polygon and then use the modifier keys to
  266. // mimic transitioning to restore mode
  267. m_actionDispatcher->CameraState(m_cameraState)
  268. ->MousePosition(topPolygonMidpoint.GetScreenSpace())
  269. ->MouseLButtonDown()
  270. ->MousePosition(topPolygonNext.GetScreenSpace())
  271. ->KeyboardModifierDown(KeyboardModifier::Ctrl)
  272. ->KeyboardModifierDown(KeyboardModifier::Shift)
  273. // trigger moving to RestoreMode (handled in Display of EditorWhiteBoxComponentMode)
  274. ->ExecuteBlock(
  275. [displayEntityViewport]()
  276. {
  277. displayEntityViewport();
  278. })
  279. ->ExpectEq(subMode(), WhiteBox::SubMode::EdgeRestore)
  280. // continue trying to move in RestoreMode
  281. ->MousePosition(topPolygonMidpoint.GetScreenSpace())
  282. ->MouseLButtonUp()
  283. ->KeyboardModifierUp(KeyboardModifier::Ctrl)
  284. ->KeyboardModifierUp(KeyboardModifier::Shift)
  285. // run update/draw logic again to change modes
  286. ->ExecuteBlock(
  287. [displayEntityViewport]()
  288. {
  289. displayEntityViewport();
  290. })
  291. // verify we are back in DefaultMode
  292. ->ExpectEq(subMode(), WhiteBox::SubMode::Default);
  293. }
  294. TEST_F(EditorWhiteBoxModifierTestFixture, SwitchToRestoreModeDestroysModifierWhileInteractingWithVertex)
  295. {
  296. namespace Api = WhiteBox::Api;
  297. using AzToolsFramework::ViewportInteraction::KeyboardModifier;
  298. const auto entityComponentIdPair = AZ::EntityComponentIdPair(m_whiteBoxEntityId, m_whiteBoxComponent->GetId());
  299. const auto displayEntityViewport = [whiteBoxEntityId = m_whiteBoxEntityId]()
  300. {
  301. AzFramework::EntityDebugDisplayEventBus::Event(
  302. whiteBoxEntityId, &AzFramework::EntityDebugDisplayEvents::DisplayEntityViewport,
  303. AzFramework::ViewportInfo{0}, NullDebugDisplayRequests{});
  304. };
  305. // helper to check which submode we're in
  306. const auto subMode = [entityComponentIdPair]()
  307. {
  308. WhiteBox::SubMode subMode;
  309. WhiteBox::EditorWhiteBoxComponentModeRequestBus::EventResult(
  310. subMode, entityComponentIdPair,
  311. &WhiteBox::EditorWhiteBoxComponentModeRequestBus::Events::GetCurrentSubMode);
  312. return subMode;
  313. };
  314. // the initial starting position of the entity (in front of and just below the camera)
  315. const AZ::Transform initialEntityTransformWorld =
  316. AZ::Transform::CreateTranslation(AZ::Vector3(0.0f, 7.0f, 23.0f));
  317. // grab the White Box Mesh (for use with the White Box Tool Api)
  318. WhiteBox::WhiteBoxMesh* whiteBox = nullptr;
  319. WhiteBox::EditorWhiteBoxComponentRequestBus::EventResult(
  320. whiteBox, entityComponentIdPair, &WhiteBox::EditorWhiteBoxComponentRequestBus::Events::GetWhiteBoxMesh);
  321. // move the entity to its starting position
  322. AzToolsFramework::SetWorldTransform(m_whiteBoxEntityId, initialEntityTransformWorld);
  323. // select the entity with the White Box Component
  324. AzToolsFramework::SelectEntity(m_whiteBoxEntityId);
  325. // mimic pressing the 'Edit' button on the Component Card
  326. EnterComponentMode<WhiteBox::EditorWhiteBoxComponent>();
  327. // override the default modifier key behavior for the white box component mode
  328. WhiteBox::EditorWhiteBoxComponentModeRequestBus::Event(
  329. entityComponentIdPair,
  330. &WhiteBox::EditorWhiteBoxComponentModeRequestBus::Events::OverrideKeyboardModifierQuery,
  331. [this]()
  332. {
  333. return m_actionDispatcher->QueryKeyboardModifiers();
  334. });
  335. AzFramework::SetCameraTransform(
  336. m_cameraState,
  337. AZ::Transform::CreateFromMatrix3x3AndTranslation(
  338. AZ::Matrix3x3::CreateRotationX(AZ::DegToRad(-45.0f)), AZ::Vector3(0.0f, 4.0f, 26.0f)));
  339. const AZ::Vector3 vertexLocalPosition = Api::VertexPosition(*whiteBox, Api::VertexHandle(1));
  340. const AZ::Vector3 topPolygonNextPositionLocal = vertexLocalPosition + AZ::Vector3::CreateAxisZ(0.5f);
  341. // the middle of the top
  342. MultiSpacePoint topPolygonMidpoint(vertexLocalPosition, initialEntityTransformWorld, m_cameraState);
  343. MultiSpacePoint topPolygonNext(topPolygonNextPositionLocal, initialEntityTransformWorld, m_cameraState);
  344. // begin interacting with a polygon and then use the modifier keys to
  345. // mimic transitioning to restore mode
  346. m_actionDispatcher->CameraState(m_cameraState)
  347. ->MousePosition(topPolygonMidpoint.GetScreenSpace())
  348. ->MouseLButtonDown()
  349. ->MousePosition(topPolygonNext.GetScreenSpace())
  350. ->KeyboardModifierDown(KeyboardModifier::Ctrl)
  351. ->KeyboardModifierDown(KeyboardModifier::Shift)
  352. // trigger moving to RestoreMode (handled in Display of EditorWhiteBoxComponentMode)
  353. ->ExecuteBlock(
  354. [displayEntityViewport]()
  355. {
  356. displayEntityViewport();
  357. })
  358. ->ExpectEq(subMode(), WhiteBox::SubMode::EdgeRestore)
  359. // continue trying to move in RestoreMode
  360. ->MousePosition(topPolygonMidpoint.GetScreenSpace())
  361. ->MouseLButtonUp()
  362. ->KeyboardModifierUp(KeyboardModifier::Ctrl)
  363. ->KeyboardModifierUp(KeyboardModifier::Shift)
  364. // run update/draw logic again to change modes
  365. ->ExecuteBlock(
  366. [displayEntityViewport]()
  367. {
  368. displayEntityViewport();
  369. })
  370. // verify we are back in DefaultMode
  371. ->ExpectEq(subMode(), WhiteBox::SubMode::Default);
  372. }
  373. TEST_F(EditorWhiteBoxModifierTestFixture, SelectedVertexModifierIsCleanedUpAfterDefaultShapeChange)
  374. {
  375. namespace Api = WhiteBox::Api;
  376. using ::testing::ElementsAre;
  377. // the initial starting position of the entity (in front of and just below the camera)
  378. const AZ::Transform initialEntityTransformWorld =
  379. AZ::Transform::CreateTranslation(AZ::Vector3(0.0f, 8.0f, 23.0f));
  380. const auto entityComponentIdPair = AZ::EntityComponentIdPair(m_whiteBoxEntityId, m_whiteBoxComponent->GetId());
  381. // grab the White Box Mesh (for use with the White Box Tool Api)
  382. WhiteBox::WhiteBoxMesh* whiteBox = nullptr;
  383. WhiteBox::EditorWhiteBoxComponentRequestBus::EventResult(
  384. whiteBox, entityComponentIdPair, &WhiteBox::EditorWhiteBoxComponentRequestBus::Events::GetWhiteBoxMesh);
  385. // create a 3x3 grid from the starting cube
  386. Initialize3x3CubeGrid(*whiteBox);
  387. // move the entity to its starting position
  388. AzToolsFramework::SetWorldTransform(m_whiteBoxEntityId, initialEntityTransformWorld);
  389. // select the entity with the White Box Component
  390. AzToolsFramework::SelectEntity(m_whiteBoxEntityId);
  391. // mimic pressing the 'Edit' button on the Component Card
  392. EnterComponentMode<WhiteBox::EditorWhiteBoxComponent>();
  393. AzFramework::SetCameraTransform(
  394. m_cameraState, AZ::Transform::CreateTranslation(AZ::Vector3(0.0f, 0.0f, 25.0f)));
  395. // arbitrary vertex (top, right corner of the tessellated box)
  396. MultiSpacePoint vertexHandle14Position(
  397. Api::VertexPosition(*whiteBox, Api::VertexHandle(14)), initialEntityTransformWorld, m_cameraState);
  398. // select the vertex
  399. m_actionDispatcher->CameraState(m_cameraState)
  400. ->MousePosition(vertexHandle14Position.GetScreenSpace())
  401. ->MouseLButtonDown()
  402. ->MouseLButtonUp();
  403. // little wrapper for EBus call
  404. const auto selectedVertexHandles = [entityComponentIdPair]()
  405. {
  406. Api::VertexHandles selectedVertexHandles;
  407. WhiteBox::EditorWhiteBoxDefaultModeRequestBus::EventResult(
  408. selectedVertexHandles, entityComponentIdPair,
  409. &WhiteBox::EditorWhiteBoxDefaultModeRequestBus::Events::SelectedVertexHandles);
  410. return selectedVertexHandles;
  411. };
  412. // given
  413. // verify the vertex is selected
  414. EXPECT_THAT(selectedVertexHandles(), ElementsAre(Api::VertexHandle(14)));
  415. // when
  416. // change the default shape
  417. WhiteBox::EditorWhiteBoxComponentRequestBus::Event(
  418. entityComponentIdPair, &WhiteBox::EditorWhiteBoxComponentRequestBus::Events::SetDefaultShape,
  419. WhiteBox::DefaultShapeType::Cylinder);
  420. // selected vertices have been cleared
  421. EXPECT_THAT(selectedVertexHandles(), ElementsAre());
  422. }
  423. TEST_F(EditorWhiteBoxModifierTestFixture, HiddenVertexCannotBeHoveredInDefaultMode)
  424. {
  425. namespace Api = WhiteBox::Api;
  426. // the initial starting position of the entity (in front just below the camera)
  427. const AZ::Transform initialEntityTransformWorld =
  428. AZ::Transform::CreateTranslation(AZ::Vector3(0.0f, 8.0f, 23.0f));
  429. const auto entityComponentIdPair = AZ::EntityComponentIdPair(m_whiteBoxEntityId, m_whiteBoxComponent->GetId());
  430. // grab the White Box Mesh (for use with the White Box Tool Api)
  431. WhiteBox::WhiteBoxMesh* whiteBox = nullptr;
  432. WhiteBox::EditorWhiteBoxComponentRequestBus::EventResult(
  433. whiteBox, entityComponentIdPair, &WhiteBox::EditorWhiteBoxComponentRequestBus::Events::GetWhiteBoxMesh);
  434. // move the entity to its starting position
  435. AzToolsFramework::SetWorldTransform(m_whiteBoxEntityId, initialEntityTransformWorld);
  436. // select the entity with the White Box Component
  437. AzToolsFramework::SelectEntity(m_whiteBoxEntityId);
  438. // mimic pressing the 'Edit' button on the Component Card
  439. EnterComponentMode<WhiteBox::EditorWhiteBoxComponent>();
  440. AzFramework::SetCameraTransform(
  441. m_cameraState,
  442. AZ::Transform::CreateFromMatrix3x3AndTranslation(
  443. AZ::Matrix3x3::CreateRotationX(AZ::DegToRad(-45.0f)), AZ::Vector3(0.0f, 4.0f, 26.0f)));
  444. // given
  445. // create a 3x3 grid from the starting cube and hide all top edges
  446. Initialize3x3CubeGrid(*whiteBox);
  447. // calculate screen space positions for vertices
  448. MultiSpacePoint vertexHandle20Position(
  449. Api::VertexPosition(*whiteBox, Api::VertexHandle(20)), initialEntityTransformWorld, m_cameraState);
  450. MultiSpacePoint vertexHandle0Position(
  451. Api::VertexPosition(*whiteBox, Api::VertexHandle(0)), initialEntityTransformWorld, m_cameraState);
  452. MultiSpacePoint vertexHandle11Position(
  453. Api::VertexPosition(*whiteBox, Api::VertexHandle(11)), initialEntityTransformWorld, m_cameraState);
  454. MultiSpacePoint vertexHandle16Position(
  455. Api::VertexPosition(*whiteBox, Api::VertexHandle(16)), initialEntityTransformWorld, m_cameraState);
  456. struct MultiSpacePointVertexHandlePair
  457. {
  458. MultiSpacePoint m_multiSpacePoint;
  459. Api::VertexHandle m_vertexHandle;
  460. };
  461. // associate vertex handles and positions
  462. const MultiSpacePointVertexHandlePair multiSpacePointVertexHandlePairs[] = {
  463. {vertexHandle20Position, Api::VertexHandle(20)},
  464. {vertexHandle0Position, Api::VertexHandle(0)},
  465. {vertexHandle11Position, Api::VertexHandle(11)},
  466. {vertexHandle16Position, Api::VertexHandle(16)}};
  467. m_actionDispatcher->CameraState(m_cameraState);
  468. // wrapper for bus call
  469. const auto hoveredVertexHandle = [entityComponentIdPair]()
  470. {
  471. Api::VertexHandle hoveredVertexHandle;
  472. WhiteBox::EditorWhiteBoxDefaultModeRequestBus::EventResult(
  473. hoveredVertexHandle, entityComponentIdPair,
  474. &WhiteBox::EditorWhiteBoxDefaultModeRequestBus::Events::HoveredVertexHandle);
  475. return hoveredVertexHandle;
  476. };
  477. // check all vertices are tracked as hovered (before hiding edges)
  478. for (const auto& [multiSpacePoint, vertexHandle] : multiSpacePointVertexHandlePairs)
  479. {
  480. m_actionDispatcher->MousePosition(multiSpacePoint.GetScreenSpace());
  481. EXPECT_EQ(hoveredVertexHandle(), vertexHandle);
  482. }
  483. // when
  484. HideAllTopUserEdgesFor3x3Grid(*whiteBox);
  485. // then
  486. // check hovering over a vertex no longer returns the handle (as there are no connecting edges)
  487. for (const auto& [multiSpacePoint, vertexHandle] : multiSpacePointVertexHandlePairs)
  488. {
  489. m_actionDispatcher->MousePosition(multiSpacePoint.GetScreenSpace());
  490. EXPECT_EQ(hoveredVertexHandle(), Api::VertexHandle());
  491. }
  492. }
  493. AZStd::optional<AZStd::string> RelativePathNullopt([[maybe_unused]] const AZStd::string& absolutePath)
  494. {
  495. return AZStd::nullopt;
  496. }
  497. int SaveDecisionAccept()
  498. {
  499. return QMessageBox::Save;
  500. }
  501. int SaveDecisionCancel()
  502. {
  503. return QMessageBox::Cancel;
  504. }
  505. TEST_F(EditorWhiteBoxComponentTestFixture, TrySaveEmptyWhiteBoxAssetPathCancelsSave)
  506. {
  507. using testing::IsFalse;
  508. const auto absoluteSavePathFn = []([[maybe_unused]] const AZStd::string& initialAbsolutePath)
  509. {
  510. return AZStd::string();
  511. };
  512. const AZStd::optional<WhiteBox::WhiteBoxSaveResult> saveResult =
  513. WhiteBox::TrySaveAs("Entity1", absoluteSavePathFn, &RelativePathNullopt, &SaveDecisionCancel);
  514. EXPECT_FALSE(saveResult.has_value());
  515. }
  516. inline ::testing::PolymorphicMatcher<::testing::internal::StrEqualityMatcher<AZStd::string>> StrEq(
  517. const AZStd::string& str)
  518. {
  519. return ::testing::MakePolymorphicMatcher(
  520. ::testing::internal::StrEqualityMatcher<AZStd::string>(str, true, true));
  521. }
  522. TEST_F(EditorWhiteBoxComponentTestFixture, TrySaveWhiteBoxAssetCanBeSavedInsideProjectFolder)
  523. {
  524. AZStd::string absolutePath;
  525. const auto absoluteSavePathFn = [&absolutePath](const AZStd::string& initialAbsolutePath)
  526. {
  527. absolutePath = initialAbsolutePath;
  528. return absolutePath;
  529. };
  530. AZStd::string relativePath;
  531. const auto relativePathSuccessFn =
  532. [&relativePath]([[maybe_unused]] const AZStd::string& absolutePath) -> AZStd::optional<AZStd::string>
  533. {
  534. // return relative path as if the asset was at the root of the project
  535. AZ::IO::Path path(absolutePath);
  536. relativePath = AZ::IO::Path(path.Filename()).String();
  537. return relativePath;
  538. };
  539. const AZStd::optional<WhiteBox::WhiteBoxSaveResult> saveResult =
  540. WhiteBox::TrySaveAs("Entity1", absoluteSavePathFn, relativePathSuccessFn, &SaveDecisionAccept);
  541. EXPECT_TRUE(saveResult.has_value());
  542. EXPECT_THAT(saveResult->m_absoluteFilePath, StrEq(absolutePath));
  543. EXPECT_THAT(saveResult->m_relativeAssetPath.value(), StrEq(relativePath));
  544. }
  545. TEST_F(EditorWhiteBoxComponentTestFixture, TrySaveWhiteBoxAssetCanBeSavedOutsideProjectFolder)
  546. {
  547. AZStd::string absolutePath;
  548. const auto absoluteSavePathFn = [&absolutePath](const AZStd::string& initialAbsolutePath)
  549. {
  550. absolutePath = initialAbsolutePath;
  551. return absolutePath;
  552. };
  553. const auto relativePathFailureFn = []([[maybe_unused]] const AZStd::string& absolutePath)
  554. {
  555. return AZStd::nullopt;
  556. };
  557. const AZStd::optional<WhiteBox::WhiteBoxSaveResult> saveResult =
  558. WhiteBox::TrySaveAs("Entity1", absoluteSavePathFn, relativePathFailureFn, &SaveDecisionAccept);
  559. EXPECT_TRUE(saveResult.has_value());
  560. EXPECT_THAT(saveResult->m_absoluteFilePath, StrEq(absolutePath));
  561. EXPECT_FALSE(saveResult->m_relativeAssetPath.has_value());
  562. }
  563. TEST_F(EditorWhiteBoxComponentTestFixture, TrySaveWhiteBoxAssetCancelOutsideProjectFolder)
  564. {
  565. const auto absoluteSavePathFn = []([[maybe_unused]] const AZStd::string& initialAbsolutePath)
  566. {
  567. return initialAbsolutePath;
  568. };
  569. const auto relativePathFailureFn =
  570. []([[maybe_unused]] const AZStd::string& absolutePath) -> AZStd::optional<AZStd::string>
  571. {
  572. return AZStd::nullopt;
  573. };
  574. const AZStd::optional<WhiteBox::WhiteBoxSaveResult> saveResult =
  575. WhiteBox::TrySaveAs("Entity1", absoluteSavePathFn, relativePathFailureFn, &SaveDecisionCancel);
  576. EXPECT_FALSE(saveResult.has_value());
  577. }
  578. class EditorWhiteBoxAssetFixture : public ToolsApplicationFixture<>
  579. {
  580. public:
  581. void SetUpEditorFixtureImpl() override;
  582. void TearDownEditorFixtureImpl() override;
  583. WhiteBox::Api::WhiteBoxMeshPtr m_whiteBox;
  584. WhiteBox::EditorWhiteBoxMeshAsset* m_whiteBoxMeshAsset;
  585. WhiteBox::EditorWhiteBoxComponent* m_editorWhiteBoxSystemComponent = nullptr;
  586. AZStd::unique_ptr<AZ::ComponentDescriptor> m_editorWhiteBoxComponentDescriptor;
  587. AZStd::unique_ptr<AZ::ComponentDescriptor> m_editorWhiteBoxSystemComponentDescriptor;
  588. };
  589. void EditorWhiteBoxAssetFixture::SetUpEditorFixtureImpl()
  590. {
  591. AZ::SerializeContext* serializeContext = nullptr;
  592. AZ::ComponentApplicationBus::BroadcastResult(
  593. serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
  594. m_editorWhiteBoxComponentDescriptor =
  595. AZStd::unique_ptr<AZ::ComponentDescriptor>(WhiteBox::EditorWhiteBoxComponent::CreateDescriptor());
  596. m_editorWhiteBoxSystemComponentDescriptor =
  597. AZStd::unique_ptr<AZ::ComponentDescriptor>(WhiteBox::EditorWhiteBoxSystemComponent::CreateDescriptor());
  598. m_editorWhiteBoxComponentDescriptor->Reflect(serializeContext);
  599. m_editorWhiteBoxSystemComponentDescriptor->Reflect(serializeContext);
  600. m_whiteBox = WhiteBox::Api::CreateWhiteBoxMesh();
  601. WhiteBox::Api::InitializeAsUnitCube(*m_whiteBox);
  602. m_whiteBoxMeshAsset = aznew WhiteBox::EditorWhiteBoxMeshAsset();
  603. m_whiteBoxMeshAsset->Associate(AZ::EntityComponentIdPair{});
  604. AZ::Entity* systemEntity = nullptr;
  605. AZ::ComponentApplicationBus::BroadcastResult(
  606. systemEntity, &AZ::ComponentApplicationBus::Events::FindEntity, AZ::SystemEntityId);
  607. systemEntity->Deactivate();
  608. systemEntity->AddComponent(m_editorWhiteBoxSystemComponentDescriptor->CreateComponent());
  609. systemEntity->Activate();
  610. }
  611. void EditorWhiteBoxAssetFixture::TearDownEditorFixtureImpl()
  612. {
  613. delete m_whiteBoxMeshAsset;
  614. m_whiteBox.reset();
  615. m_editorWhiteBoxSystemComponentDescriptor.reset();
  616. m_editorWhiteBoxComponentDescriptor.reset();
  617. }
  618. TEST_F(EditorWhiteBoxAssetFixture, WhiteBoxAssetCanBeCreatedFromWhiteBoxMesh)
  619. {
  620. // verify preconditions
  621. EXPECT_FALSE(m_whiteBoxMeshAsset->InUse());
  622. m_whiteBoxMeshAsset->TakeOwnershipOfWhiteBoxMesh("test-asset", AZStd::move(m_whiteBox));
  623. // asset is created immediately from the white box mesh
  624. EXPECT_TRUE(m_whiteBoxMeshAsset->InUse());
  625. EXPECT_TRUE(m_whiteBoxMeshAsset->Loaded());
  626. EXPECT_TRUE(m_whiteBoxMeshAsset->GetWhiteBoxMeshAssetId().IsValid());
  627. }
  628. TEST_F(EditorWhiteBoxAssetFixture, WhiteBoxAssetCanBeSerialized)
  629. {
  630. namespace Api = WhiteBox::Api;
  631. using ::testing::Eq;
  632. using ::testing::NotNull;
  633. const auto whiteBoxCopy = Api::CloneMesh(*m_whiteBox);
  634. m_whiteBoxMeshAsset->TakeOwnershipOfWhiteBoxMesh("test-asset", AZStd::move(m_whiteBox));
  635. m_whiteBoxMeshAsset->Serialize();
  636. const auto meshAsset = m_whiteBoxMeshAsset->GetWhiteBoxMeshAsset();
  637. Api::WhiteBoxMeshPtr deserializedWhiteBox = Api::CreateWhiteBoxMesh();
  638. Api::ReadMesh(*deserializedWhiteBox, meshAsset->GetWhiteBoxData());
  639. EXPECT_THAT(deserializedWhiteBox, NotNull());
  640. EXPECT_THAT(Api::MeshVertexCount(*deserializedWhiteBox), Eq(Api::MeshVertexCount(*whiteBoxCopy)));
  641. EXPECT_THAT(Api::MeshVertexHandles(*deserializedWhiteBox), Eq(Api::MeshVertexHandles(*whiteBoxCopy)));
  642. EXPECT_THAT(Api::MeshFaceHandles(*deserializedWhiteBox), Eq(Api::MeshFaceHandles(*whiteBoxCopy)));
  643. EXPECT_THAT(Api::MeshEdgeHandles(*deserializedWhiteBox), Eq(Api::MeshEdgeHandles(*whiteBoxCopy)));
  644. EXPECT_THAT(Api::MeshHalfedgeCount(*deserializedWhiteBox), Eq(Api::MeshHalfedgeCount(*whiteBoxCopy)));
  645. EXPECT_THAT(Api::MeshFaces(*deserializedWhiteBox), Eq(Api::MeshFaces(*whiteBoxCopy)));
  646. }
  647. TEST_F(EditorWhiteBoxAssetFixture, WhiteBoxAssetCanBeCleared)
  648. {
  649. using ::testing::Eq;
  650. using ::testing::IsFalse;
  651. using ::testing::IsNull;
  652. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
  653. // Given
  654. // ensure the White Box request bus only returns a null render mesh
  655. WhiteBox::WhiteBoxRequestBus::Broadcast(
  656. &WhiteBox::WhiteBoxRequestBus::Events::SetRenderMeshInterfaceBuilder,
  657. [](AZ::EntityId entityId)
  658. {
  659. return AZStd::make_unique<WhiteBox::WhiteBoxNullRenderMesh>(entityId);
  660. });
  661. // create an editor entity with a White Box component on it
  662. auto editorEntityAndWhiteBox = CreateEditorEntityWithEditorWhiteBoxComponent();
  663. auto whiteBoxComponent = editorEntityAndWhiteBox.m_editorWhiteBoxComponent;
  664. auto entityId = editorEntityAndWhiteBox.GetEntityId();
  665. // install our own EditorWhiteBoxMeshAsset
  666. whiteBoxComponent->OverrideEditorWhiteBoxMeshAsset(m_whiteBoxMeshAsset);
  667. m_whiteBoxMeshAsset->Associate(AZ::EntityComponentIdPair{entityId, whiteBoxComponent->GetId()});
  668. // change shape type to asset (equivalent to picking Asset from Entity Inspector)
  669. whiteBoxComponent->SetDefaultShape(WhiteBox::DefaultShapeType::Asset);
  670. // initialize the asset with our own White Box mesh
  671. m_whiteBoxMeshAsset->TakeOwnershipOfWhiteBoxMesh("test-asset", AZStd::move(m_whiteBox));
  672. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
  673. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
  674. // When
  675. // change back to a set shape (no longer using Asset)
  676. whiteBoxComponent->SetDefaultShape(WhiteBox::DefaultShapeType::Cube);
  677. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
  678. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
  679. // Then
  680. // ensure the EditorWhiteBoxMeshAsset was correctly cleared and unset
  681. EXPECT_THAT(m_whiteBoxMeshAsset->GetWhiteBoxMesh(), IsNull());
  682. EXPECT_THAT(m_whiteBoxMeshAsset->GetWhiteBoxMeshAssetId(), Eq(AZ::Data::AssetId()));
  683. EXPECT_THAT(m_whiteBoxMeshAsset->InUse(), IsFalse());
  684. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
  685. // cleanup
  686. AzToolsFramework::EditorEntityContextRequestBus::Broadcast(
  687. &AzToolsFramework::EditorEntityContextRequestBus::Events::DestroyEditorEntity, entityId);
  688. // now owned by EditorWhiteBoxComponent so ensure we do not attempt to delete it ourselves
  689. m_whiteBoxMeshAsset = nullptr;
  690. }
  691. TEST_F(EditorWhiteBoxAssetFixture, EditorWhiteBoxMeshAssetNotClearedWhenDeactivatingAndActivatingEntity)
  692. {
  693. using ::testing::IsTrue;
  694. using ::testing::NotNull;
  695. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
  696. // Given
  697. // ensure the White Box request bus only returns a null render mesh
  698. WhiteBox::WhiteBoxRequestBus::Broadcast(
  699. &WhiteBox::WhiteBoxRequestBus::Events::SetRenderMeshInterfaceBuilder,
  700. [](AZ::EntityId entityId)
  701. {
  702. return AZStd::make_unique<WhiteBox::WhiteBoxNullRenderMesh>(entityId);
  703. });
  704. // create an editor entity with a White Box component on it
  705. auto editorEntityAndWhiteBox = CreateEditorEntityWithEditorWhiteBoxComponent();
  706. auto* entity = editorEntityAndWhiteBox.m_entity;
  707. auto* whiteBoxComponent = editorEntityAndWhiteBox.m_editorWhiteBoxComponent;
  708. auto entityId = editorEntityAndWhiteBox.GetEntityId();
  709. // install our own EditorWhiteBoxMeshAsset
  710. whiteBoxComponent->OverrideEditorWhiteBoxMeshAsset(m_whiteBoxMeshAsset);
  711. m_whiteBoxMeshAsset->Associate(AZ::EntityComponentIdPair{entityId, whiteBoxComponent->GetId()});
  712. // initialize the asset with our own White Box mesh
  713. m_whiteBoxMeshAsset->TakeOwnershipOfWhiteBoxMesh("test-asset", AZStd::move(m_whiteBox));
  714. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
  715. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
  716. // When
  717. entity->Deactivate();
  718. entity->Activate();
  719. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
  720. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
  721. // Then
  722. EXPECT_THAT(whiteBoxComponent->AssetInUse(), IsTrue());
  723. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
  724. // cleanup
  725. AzToolsFramework::EditorEntityContextRequestBus::Broadcast(
  726. &AzToolsFramework::EditorEntityContextRequestBus::Events::DestroyEditorEntity, entityId);
  727. // now owned by EditorWhiteBoxComponent so ensure we do not attempt to delete it ourselves
  728. m_whiteBoxMeshAsset = nullptr;
  729. }
  730. } // namespace UnitTest