3
0

CanAddSimulatedObject.cpp 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  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 <gtest/gtest.h>
  9. #include <QPushButton>
  10. #include <QAction>
  11. #include <QtTest>
  12. #include <Tests/UI/UIFixture.h>
  13. #include <Tests/UI/MenuUIFixture.h>
  14. #include <Tests/UI/ModalPopupHandler.h>
  15. #include <EMotionFX/CommandSystem/Source/CommandManager.h>
  16. #include <EMotionFX/Source/Actor.h>
  17. #include <Editor/ColliderContainerWidget.h>
  18. #include <EMotionStudio/EMStudioSDK/Source/EMStudioManager.h>
  19. #include <EMotionStudio/EMStudioSDK/Source/PluginManager.h>
  20. #include <Editor/Plugins/SimulatedObject/SimulatedObjectWidget.h>
  21. #include <Editor/Plugins/ColliderWidgets/SimulatedObjectColliderWidget.h>
  22. #include <Editor/InputDialogValidatable.h>
  23. #include <Editor/Plugins/SkeletonOutliner/SkeletonOutlinerPlugin.h>
  24. #include <Tests/TestAssetCode/SimpleActors.h>
  25. #include <Tests/TestAssetCode/ActorFactory.h>
  26. #include <Tests/TestAssetCode/TestActorAssets.h>
  27. #include <Tests/PhysicsSetupUtils.h>
  28. #include <Tests/UI/SkeletonOutlinerTestFixture.h>
  29. #include <Editor/ReselectingTreeView.h>
  30. #include <AzQtComponents/Components/Widgets/CardHeader.h>
  31. namespace EMotionFX
  32. {
  33. class CanAddSimulatedObjectFixture
  34. : public SkeletonOutlinerTestFixture
  35. {
  36. public:
  37. void TearDown() override
  38. {
  39. QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
  40. m_actorAsset.Reset();
  41. UIFixture::TearDown();
  42. }
  43. void RecursiveGetAllChildren(QTreeView* treeView, const QModelIndex& index, QModelIndexList& outIndicies)
  44. {
  45. outIndicies.push_back(index);
  46. for (int i = 0; i < treeView->model()->rowCount(index); ++i)
  47. {
  48. RecursiveGetAllChildren(treeView, treeView->model()->index(i, 0, index), outIndicies);
  49. }
  50. }
  51. void CreateSimulateObject(const char* objectName)
  52. {
  53. // Select the newly created actor
  54. {
  55. AZStd::string result;
  56. EXPECT_TRUE(CommandSystem::GetCommandManager()->ExecuteCommand("Select -actorID 0", result)) << result.c_str();
  57. }
  58. // Change the Editor mode to Simulated Objects
  59. EMStudio::GetMainWindow()->ApplicationModeChanged("SimulatedObjects");
  60. // Find the Simulated Object Manager and its button
  61. m_simulatedObjectWidget = EMStudio::GetPluginManager()->FindActivePlugin<EMotionFX::SimulatedObjectWidget>();
  62. ASSERT_TRUE(m_simulatedObjectWidget) << "Simulated Object plugin not found!";
  63. QPushButton* addSimulatedObjectButton = m_simulatedObjectWidget->GetDockWidget()->findChild<QPushButton*>("addSimulatedObjectButton");
  64. // Send the left button click directly to the button
  65. QTest::mouseClick(addSimulatedObjectButton, Qt::LeftButton);
  66. // In the Input Dialog set the name of the object and close the dialog
  67. EMStudio::InputDialogValidatable* inputDialog = qobject_cast<EMStudio::InputDialogValidatable*>(FindTopLevelWidget("EMFX.SimulatedObjectActionManager.SimulatedObjectDialog"));
  68. ASSERT_NE(inputDialog, nullptr) << "Cannot find input dialog.";
  69. inputDialog->SetText(objectName);
  70. inputDialog->accept();
  71. // There is one and only one simulated objects
  72. ASSERT_EQ(m_actorAsset->GetActor()->GetSimulatedObjectSetup()->GetNumSimulatedObjects(), 1);
  73. // Check it has the correct name
  74. EXPECT_STREQ(m_actorAsset->GetActor()->GetSimulatedObjectSetup()->GetSimulatedObject(0)->GetName().c_str(), objectName);
  75. }
  76. void AddCapsuleColliderToJointIndex(int index)
  77. {
  78. m_skeletonTreeView->selectionModel()->clearSelection();
  79. // Find the indexed joint in the TreeView and select it
  80. SelectIndexes(m_indexList, m_skeletonTreeView, index, index);
  81. // Open the Right Click Context Menu
  82. const QRect rect = m_skeletonTreeView->visualRect(m_indexList[index-1]);
  83. EXPECT_TRUE(rect.isValid());
  84. BringUpContextMenu(m_skeletonTreeView, rect);
  85. QMenu* contextMenu = m_skeletonOutliner->GetDockWidget()->findChild<QMenu*>("contextMenu");
  86. // Trace down the sub Menus to Add Collider and select it
  87. QAction* simulatedObjectColliderAction = GetNamedAction(m_skeletonOutliner->GetDockWidget(), "Add collider");
  88. ASSERT_TRUE(simulatedObjectColliderAction);
  89. QMenu* simulatedObjectColliderMenu = simulatedObjectColliderAction->menu();
  90. const QList<QAction*> addSelectedColliderMenuActions = simulatedObjectColliderMenu->actions();
  91. auto addCapsuleColliderAction = AZStd::find_if(addSelectedColliderMenuActions.begin(), addSelectedColliderMenuActions.end(), [](const QAction* action) {
  92. return action->text() == "Capsule";
  93. });
  94. ASSERT_NE(addCapsuleColliderAction, addSelectedColliderMenuActions.end());
  95. size_t numCapsuleColliders = PhysicsSetupUtils::CountColliders(
  96. m_actorAsset->GetActor(), PhysicsSetup::SimulatedObjectCollider, false, Physics::ShapeType::Capsule);
  97. (*addCapsuleColliderAction)->trigger();
  98. // Delete the context menu, otherwise there it will still be around during this frame as the Qt event loop has not been run.
  99. delete contextMenu;
  100. const size_t numCapsuleCollidersAfterAdd = PhysicsSetupUtils::CountColliders(
  101. m_actorAsset->GetActor(), PhysicsSetup::SimulatedObjectCollider, false, Physics::ShapeType::Capsule);
  102. ASSERT_EQ(numCapsuleCollidersAfterAdd, numCapsuleColliders + 1);
  103. m_skeletonTreeView->selectionModel()->clearSelection();
  104. }
  105. protected:
  106. AZ::Data::Asset<Integration::ActorAsset> m_actorAsset;
  107. EMotionFX::SimulatedObjectWidget* m_simulatedObjectWidget = nullptr;
  108. EMotionFX::SkeletonOutlinerPlugin* m_skeletonOutliner = nullptr;
  109. ReselectingTreeView* m_skeletonTreeView = nullptr;
  110. const QAbstractItemModel* m_skeletonModel;
  111. QModelIndexList m_indexList;
  112. };
  113. TEST_F(CanAddSimulatedObjectFixture, CanAddSimulatedObject)
  114. {
  115. RecordProperty("test_case_id", "C13048820");
  116. AZ::Data::AssetId actorAssetId("{5060227D-B6F4-422E-BF82-41AAC5F228A5}");
  117. m_actorAsset = TestActorAssets::CreateActorAssetAndRegister<SimpleJointChainActor>(actorAssetId, 1, "CanAddSimulatedObjectActor");
  118. CreateSimulateObject("New simulated object");
  119. }
  120. TEST_F(UIFixture, CanAddSimulatedObjectWithJoints)
  121. {
  122. RecordProperty("test_case_id", "C13048818");
  123. AZ::Data::AssetId actorAssetId("{5060227D-B6F4-422E-BF82-41AAC5F228A5}");
  124. AZ::Data::Asset<Integration::ActorAsset> actorAsset =
  125. TestActorAssets::CreateActorAssetAndRegister<SimpleJointChainActor>(actorAssetId, 2, "CanAddSimulatedObjectWithJointsActor");
  126. Actor* actor = actorAsset->GetActor();
  127. ActorInstance* actorInstance = ActorInstance::Create(actor);
  128. EMStudio::GetMainWindow()->ApplicationModeChanged("SimulatedObjects");
  129. {
  130. AZStd::string result;
  131. EXPECT_TRUE(CommandSystem::GetCommandManager()->ExecuteCommand(AZStd::string{"Select -actorInstanceID "} + AZStd::to_string(actorInstance->GetID()), result)) << result.c_str();
  132. }
  133. auto simulatedObjectWidget = static_cast<EMotionFX::SimulatedObjectWidget*>(EMStudio::GetPluginManager()->FindActivePlugin(EMotionFX::SimulatedObjectWidget::CLASS_ID));
  134. ASSERT_TRUE(simulatedObjectWidget) << "Simulated Object plugin not found!";
  135. auto skeletonOutliner = static_cast<EMotionFX::SkeletonOutlinerPlugin*>(EMStudio::GetPluginManager()->FindActivePlugin(EMotionFX::SkeletonOutlinerPlugin::CLASS_ID));
  136. QTreeView* treeView = skeletonOutliner->GetDockWidget()->findChild<QTreeView*>("EMFX.SkeletonOutlinerPlugin.SkeletonOutlinerTreeView");
  137. const QAbstractItemModel* model = treeView->model();
  138. const QModelIndex rootJointIndex = model->index(0, 0, model->index(0, 0));
  139. ASSERT_TRUE(rootJointIndex.isValid()) << "Unable to find a model index for the root joint of the actor";
  140. treeView->selectionModel()->select(rootJointIndex, QItemSelectionModel::Select | QItemSelectionModel::Rows);
  141. treeView->scrollTo(rootJointIndex);
  142. BringUpContextMenu(treeView, treeView->visualRect(rootJointIndex));
  143. QMenu* contextMenu = skeletonOutliner->GetDockWidget()->findChild<QMenu*>("EMFX.SkeletonOutlinerPlugin.ContextMenu");
  144. EXPECT_TRUE(contextMenu);
  145. QAction* addSelectedJointAction;
  146. EXPECT_TRUE(UIFixture::GetActionFromContextMenu(addSelectedJointAction, contextMenu, "Add to simulated object"));
  147. QMenu* addSelectedJointMenu = addSelectedJointAction->menu();
  148. EXPECT_TRUE(addSelectedJointMenu);
  149. QAction* newSimulatedObjectAction;
  150. EXPECT_TRUE(UIFixture::GetActionFromContextMenu(newSimulatedObjectAction, addSelectedJointMenu, "New simulated object..."));
  151. // Handle the add children dialog box.
  152. ModalPopupHandler messageBoxPopupHandler;
  153. messageBoxPopupHandler.WaitForPopupPressDialogButton<QMessageBox*>(QDialogButtonBox::No);
  154. newSimulatedObjectAction->trigger();
  155. EMStudio::InputDialogValidatable* inputDialog = qobject_cast<EMStudio::InputDialogValidatable*>(FindTopLevelWidget("EMFX.SimulatedObjectActionManager.SimulatedObjectDialog"));
  156. ASSERT_NE(inputDialog, nullptr) << "Cannot find input dialog.";
  157. inputDialog->SetText("New simulated object");
  158. inputDialog->accept();
  159. ASSERT_EQ(actor->GetSimulatedObjectSetup()->GetNumSimulatedObjects(), 1);
  160. const auto simulatedObject = actor->GetSimulatedObjectSetup()->GetSimulatedObject(0);
  161. EXPECT_STREQ(simulatedObject->GetName().c_str(), "New simulated object");
  162. EXPECT_EQ(simulatedObject->GetNumSimulatedRootJoints(), 1);
  163. EXPECT_EQ(simulatedObject->GetNumSimulatedJoints(), 1);
  164. EXPECT_STREQ(actor->GetSkeleton()->GetNode(simulatedObject->GetSimulatedJoint(0)->GetSkeletonJointIndex())->GetName(), "rootJoint");
  165. EXPECT_STREQ(actor->GetSkeleton()->GetNode(simulatedObject->GetSimulatedRootJoint(0)->GetSkeletonJointIndex())->GetName(), "rootJoint");
  166. QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
  167. actorInstance->Destroy();
  168. }
  169. TEST_F(CanAddSimulatedObjectFixture, CanAddSimulatedObjectWithJointsAndName)
  170. {
  171. RecordProperty("test_case_id", "C13048820a");
  172. AZ::Data::AssetId actorAssetId("{5060227D-B6F4-422E-BF82-41AAC5F228A5}");
  173. m_actorAsset = TestActorAssets::CreateActorAssetAndRegister<SimpleJointChainActor>(actorAssetId, 5, "CanAddSimulatedObjectActor");
  174. Actor* actor = m_actorAsset->GetActor();
  175. CreateSimulateObject("sim1");
  176. // Get the Skeleton Outliner and find the model relating to its treeview
  177. auto skeletonOutliner = static_cast<EMotionFX::SkeletonOutlinerPlugin*>(EMStudio::GetPluginManager()->FindActivePlugin(EMotionFX::SkeletonOutlinerPlugin::CLASS_ID));
  178. QTreeView* treeView = skeletonOutliner->GetDockWidget()->findChild<QTreeView*>("EMFX.SkeletonOutlinerPlugin.SkeletonOutlinerTreeView");
  179. const QAbstractItemModel* model = treeView->model();
  180. // Find the 3rd joint in the TreeView and select it
  181. const QModelIndex jointIndex = model->index(0, 3, model->index(0, 0));
  182. ASSERT_TRUE(jointIndex.isValid()) << "Unable to find a model index for the root joint of the actor";
  183. treeView->selectionModel()->select(jointIndex, QItemSelectionModel::Select | QItemSelectionModel::Rows);
  184. treeView->scrollTo(jointIndex);
  185. // Open the Right Click Context Menu
  186. const QRect rect = treeView->visualRect(jointIndex);
  187. EXPECT_TRUE(rect.isValid());
  188. BringUpContextMenu(treeView, rect);
  189. // Trace down the sub Menus to <New simulated object> and select it
  190. QMenu* contextMenu = skeletonOutliner->GetDockWidget()->findChild<QMenu*>("EMFX.SkeletonOutlinerPlugin.ContextMenu");
  191. EXPECT_TRUE(contextMenu);
  192. QAction* simulatedObjectAction;
  193. EXPECT_TRUE(UIFixture::GetActionFromContextMenu(simulatedObjectAction, contextMenu, "Add to simulated object"));
  194. QMenu* simulatedObjectMenu = simulatedObjectAction->menu();
  195. EXPECT_TRUE(simulatedObjectMenu);
  196. QAction* newSimulatedObjectAction;
  197. ASSERT_TRUE(UIFixture::GetActionFromContextMenu(newSimulatedObjectAction, simulatedObjectMenu, "New simulated object..."));
  198. // Handle the add children dialog box.
  199. ModalPopupHandler messageBoxPopupHandler;
  200. messageBoxPopupHandler.WaitForPopupPressDialogButton<QMessageBox*>(QDialogButtonBox::No);
  201. newSimulatedObjectAction->trigger();
  202. // Set the name in the Dialog Box and test it.
  203. EMStudio::InputDialogValidatable* inputDialog = qobject_cast<EMStudio::InputDialogValidatable*>(FindTopLevelWidget("EMFX.SimulatedObjectActionManager.SimulatedObjectDialog"));
  204. ASSERT_NE(inputDialog, nullptr) << "Cannot find input dialog.";
  205. inputDialog->SetText("sim2");
  206. inputDialog->accept();
  207. ASSERT_EQ(actor->GetSimulatedObjectSetup()->GetNumSimulatedObjects(), 2);
  208. const auto simulatedObject = actor->GetSimulatedObjectSetup()->GetSimulatedObject(1);
  209. EXPECT_STREQ(simulatedObject->GetName().c_str(), "sim2");
  210. }
  211. TEST_F(CanAddSimulatedObjectFixture, CanAddColliderToSimulatedObject)
  212. {
  213. RecordProperty("test_case_id", "C13048816");
  214. AZ::Data::AssetId actorAssetId("{5060227D-B6F4-422E-BF82-41AAC5F228A5}");
  215. m_actorAsset = TestActorAssets::CreateActorAssetAndRegister<SimpleJointChainActor>(actorAssetId, 5, "CanAddSimulatedObjectActor");
  216. Actor* actor = m_actorAsset->GetActor();
  217. CreateSimulateObject("sim1");
  218. // Get the Skeleton Outliner and find the model relating to its treeview
  219. auto skeletonOutliner = static_cast<EMotionFX::SkeletonOutlinerPlugin*>(EMStudio::GetPluginManager()->FindActivePlugin(EMotionFX::SkeletonOutlinerPlugin::CLASS_ID));
  220. QTreeView* treeView = skeletonOutliner->GetDockWidget()->findChild<QTreeView*>("EMFX.SkeletonOutlinerPlugin.SkeletonOutlinerTreeView");
  221. const QAbstractItemModel* model = treeView->model();
  222. // Find the 3rd joint in the TreeView and select it
  223. const QModelIndex jointIndex = model->index(0, 3, model->index(0, 0));
  224. ASSERT_TRUE(jointIndex.isValid()) << "Unable to find a model index for the root joint of the actor";
  225. treeView->selectionModel()->select(jointIndex, QItemSelectionModel::Select | QItemSelectionModel::Rows);
  226. treeView->scrollTo(jointIndex);
  227. // Open the Right Click Context Menu
  228. const QRect rect = treeView->visualRect(jointIndex);
  229. EXPECT_TRUE(rect.isValid());
  230. BringUpContextMenu(treeView, rect);
  231. // Trace down the sub Menus to Add Collider and select it
  232. QMenu* contextMenu = skeletonOutliner->GetDockWidget()->findChild<QMenu*>("EMFX.SkeletonOutlinerPlugin.ContextMenu");
  233. EXPECT_TRUE(contextMenu);
  234. QAction* addSelectedAddColliderAction;
  235. ASSERT_TRUE(UIFixture::GetActionFromContextMenu(addSelectedAddColliderAction, contextMenu, "Add collider"));
  236. QMenu* addSelectedColliderMenu = addSelectedAddColliderAction->menu();
  237. QAction* addCapsuleColliderAction;
  238. ASSERT_TRUE(UIFixture::GetActionFromContextMenu(addCapsuleColliderAction, addSelectedColliderMenu, "Capsule"));
  239. size_t numCapsuleColliders = PhysicsSetupUtils::CountColliders(actor, PhysicsSetup::SimulatedObjectCollider, false, Physics::ShapeType::Capsule);
  240. EXPECT_EQ(numCapsuleColliders, 0);
  241. addCapsuleColliderAction->trigger();
  242. // Check that a collider has been added.
  243. size_t numCollidersAfterAddCapsule = PhysicsSetupUtils::CountColliders(actor, PhysicsSetup::SimulatedObjectCollider, false, Physics::ShapeType::Capsule);
  244. ASSERT_EQ(numCollidersAfterAddCapsule, numCapsuleColliders + 1) << "Capsule collider not added.";
  245. QAction* addSphereColliderAction;
  246. ASSERT_TRUE(UIFixture::GetActionFromContextMenu(addSphereColliderAction, addSelectedColliderMenu, "Sphere"));
  247. const size_t numSphereColliders = PhysicsSetupUtils::CountColliders(actor, PhysicsSetup::SimulatedObjectCollider, false, Physics::ShapeType::Sphere);
  248. EXPECT_EQ(numSphereColliders, 0);
  249. addSphereColliderAction->trigger();
  250. // Check that a second collider has been added.
  251. const size_t numCollidersAfterAddSphere = PhysicsSetupUtils::CountColliders(actor, PhysicsSetup::SimulatedObjectCollider, false, Physics::ShapeType::Sphere);
  252. ASSERT_EQ(numCollidersAfterAddSphere, numSphereColliders + 1) << "Sphere collider not added.";
  253. }
  254. TEST_F(CanAddSimulatedObjectFixture, CanRemoveSimulatedObject)
  255. {
  256. RecordProperty("test_case_id", "C13048821");
  257. AZ::Data::AssetId actorAssetId("{5060227D-B6F4-422E-BF82-41AAC5F228A5}");
  258. m_actorAsset = TestActorAssets::CreateActorAssetAndRegister<SimpleJointChainActor>(actorAssetId, 1, "CanRemoveSimulatedObjectActor");
  259. Actor* actor = m_actorAsset->GetActor();
  260. CreateSimulateObject("TestObject1");
  261. // Get the Simulated Object and find the model relating to its treeview
  262. EMotionFX::SimulatedObjectWidget* simulatedObjectWidget = static_cast<EMotionFX::SimulatedObjectWidget*>(EMStudio::GetPluginManager()->FindActivePlugin(EMotionFX::SimulatedObjectWidget::CLASS_ID));
  263. ASSERT_TRUE(simulatedObjectWidget);
  264. QTreeView* treeView = simulatedObjectWidget->GetDockWidget()->findChild<QTreeView*>("EMFX.SimulatedObjectWidget.TreeView");
  265. ASSERT_TRUE(treeView);
  266. const SimulatedObjectModel* model = reinterpret_cast<SimulatedObjectModel*>(treeView->model());
  267. const QModelIndex index = model->index(0, 0);
  268. treeView->selectionModel()->select(index, QItemSelectionModel::Select | QItemSelectionModel::Rows);
  269. treeView->selectionModel()->setCurrentIndex(index, QItemSelectionModel::Select | QItemSelectionModel::Rows);
  270. treeView->scrollTo(index);
  271. BringUpContextMenu(treeView, treeView->visualRect(index));
  272. QMenu* contextMenu = simulatedObjectWidget->GetDockWidget()->findChild<QMenu*>("EMFX.SimulatedObjectWidget.ContextMenu");
  273. EXPECT_TRUE(contextMenu);
  274. QAction* removeObjectAction;
  275. ASSERT_TRUE(UIFixture::GetActionFromContextMenu(removeObjectAction, contextMenu, "Remove object"));
  276. removeObjectAction->trigger();
  277. ASSERT_EQ(actor->GetSimulatedObjectSetup()->GetNumSimulatedObjects(), 0);
  278. }
  279. TEST_F(CanAddSimulatedObjectFixture, CanAddColliderToSimulatedObjectFromInspector)
  280. {
  281. RecordProperty("test_case_id", "C20385259");
  282. AZ::Data::AssetId actorAssetId("{5060227D-B6F4-422E-BF82-41AAC5F228A5}");
  283. m_actorAsset = TestActorAssets::CreateActorAssetAndRegister<SimpleJointChainActor>(actorAssetId, 5, "CanAddSimulatedObjectActor");
  284. Actor* actor = m_actorAsset->GetActor();
  285. CreateSimulateObject("sim1");
  286. // Get the Skeleton Outliner and find the model relating to its treeview
  287. auto skeletonOutliner = static_cast<EMotionFX::SkeletonOutlinerPlugin*>(EMStudio::GetPluginManager()->FindActivePlugin(EMotionFX::SkeletonOutlinerPlugin::CLASS_ID));
  288. ASSERT_TRUE(skeletonOutliner);
  289. QTreeView* treeView = skeletonOutliner->GetDockWidget()->findChild<QTreeView*>("EMFX.SkeletonOutlinerPlugin.SkeletonOutlinerTreeView");
  290. ASSERT_TRUE(treeView);
  291. const QAbstractItemModel* model = treeView->model();
  292. QModelIndexList indexList;
  293. RecursiveGetAllChildren(treeView, model->index(0, 0, model->index(0, 0)), indexList);
  294. SelectIndexes(indexList, treeView, 3, 3);
  295. QDockWidget* simulatedObjectWidget = EMStudio::GetPluginManager()->FindActivePlugin<SimulatedObjectWidget>()->GetDockWidget();
  296. ASSERT_TRUE(simulatedObjectWidget);
  297. QPushButton* addColliderButton =
  298. simulatedObjectWidget->findChild<QPushButton*>("EMFX.SimulatedObjectColliderWidget.AddColliderButton");
  299. ASSERT_TRUE(addColliderButton);
  300. // Send the left button click directly to the button
  301. QTest::mouseClick(addColliderButton, Qt::LeftButton);
  302. const size_t numCapsuleColliders =
  303. PhysicsSetupUtils::CountColliders(actor, PhysicsSetup::SimulatedObjectCollider, false, Physics::ShapeType::Capsule);
  304. EXPECT_EQ(numCapsuleColliders, 0);
  305. const size_t numSphereColliders =
  306. PhysicsSetupUtils::CountColliders(actor, PhysicsSetup::SimulatedObjectCollider, false, Physics::ShapeType::Sphere);
  307. EXPECT_EQ(numSphereColliders, 0);
  308. QMenu* contextMenu = addColliderButton->findChild<QMenu*>("EMFX.AddColliderButton.ContextMenu");
  309. EXPECT_TRUE(contextMenu);
  310. QAction* addCapsuleAction;
  311. ASSERT_TRUE(UIFixture::GetActionFromContextMenu(addCapsuleAction, contextMenu, "Add capsule"));
  312. addCapsuleAction->trigger();
  313. const size_t numCollidersAfterAddCapsule =
  314. PhysicsSetupUtils::CountColliders(actor, PhysicsSetup::SimulatedObjectCollider, false, Physics::ShapeType::Capsule);
  315. ASSERT_EQ(numCollidersAfterAddCapsule, numCapsuleColliders + 1) << "Capsule collider not added.";
  316. QAction* addSphereAction;
  317. ASSERT_TRUE(UIFixture::GetActionFromContextMenu(addSphereAction, contextMenu, "Add sphere"));
  318. addSphereAction->trigger();
  319. const size_t numCollidersAfterAddSphere =
  320. PhysicsSetupUtils::CountColliders(actor, PhysicsSetup::SimulatedObjectCollider, false, Physics::ShapeType::Sphere);
  321. ASSERT_EQ(numCollidersAfterAddSphere, numSphereColliders + 1) << "Sphere collider not added.";
  322. }
  323. TEST_F(CanAddSimulatedObjectFixture, CanAddMultipleJointsToSimulatedObject)
  324. {
  325. RecordProperty("test_case_id", "C13048818");
  326. AZ::Data::AssetId actorAssetId("{5060227D-B6F4-422E-BF82-41AAC5F228A5}");
  327. m_actorAsset = TestActorAssets::CreateActorAssetAndRegister<SimpleJointChainActor>(actorAssetId, 7, "CanAddSimulatedObjectActor");
  328. Actor* actor = m_actorAsset->GetActor();
  329. CreateSimulateObject("ANY");
  330. // Get the Skeleton Outliner and find the model relating to its treeview
  331. auto skeletonOutliner = static_cast<EMotionFX::SkeletonOutlinerPlugin*>(EMStudio::GetPluginManager()->FindActivePlugin(EMotionFX::SkeletonOutlinerPlugin::CLASS_ID));
  332. ASSERT_TRUE(skeletonOutliner) << "Can't find SkeletonOutlinerPlugin";
  333. QTreeView* treeView = skeletonOutliner->GetDockWidget()->findChild<QTreeView*>("EMFX.SkeletonOutlinerPlugin.SkeletonOutlinerTreeView");
  334. ASSERT_TRUE(treeView) << "Skeleton Treeview not found";
  335. const QAbstractItemModel* model = treeView->model();
  336. QModelIndexList indexList;
  337. RecursiveGetAllChildren(treeView, model->index(0, 0, model->index(0, 0)), indexList);
  338. SelectIndexes(indexList, treeView, 3, 5);
  339. // Open the Right Click Context Menu
  340. const QRect rect = treeView->visualRect(indexList[4]);
  341. EXPECT_TRUE(rect.isValid());
  342. BringUpContextMenu(treeView, rect);
  343. // Trace down the sub Menus to <New simulated object> and select it
  344. QMenu* contextMenu = skeletonOutliner->GetDockWidget()->findChild<QMenu*>("EMFX.SkeletonOutlinerPlugin.ContextMenu");
  345. EXPECT_TRUE(contextMenu) << "Context Menu not found";
  346. QAction* simulatedObjectAction;
  347. EXPECT_TRUE(UIFixture::GetActionFromContextMenu(simulatedObjectAction, contextMenu, "Add to simulated object"));
  348. QMenu* simulatedObjectMenu = simulatedObjectAction->menu();
  349. EXPECT_TRUE(simulatedObjectMenu) << "Simulated Object Menu not found";
  350. QAction* newSimulatedObjectAction;
  351. ASSERT_TRUE(UIFixture::GetActionFromContextMenu(newSimulatedObjectAction, simulatedObjectMenu, "ANY")) << "Can't find named simulated object in menu";
  352. // Handle the add children dialog box.
  353. ModalPopupHandler messageBoxPopupHandler;
  354. messageBoxPopupHandler.WaitForPopupPressDialogButton<QMessageBox*>(QDialogButtonBox::No);
  355. newSimulatedObjectAction->trigger();
  356. const EMotionFX::SimulatedObject* simulatedObject = actor->GetSimulatedObjectSetup()->GetSimulatedObject(0);
  357. EXPECT_EQ(simulatedObject->GetNumSimulatedRootJoints(), 1);
  358. EXPECT_EQ(simulatedObject->GetNumSimulatedJoints(), 3);
  359. }
  360. TEST_F(CanAddSimulatedObjectFixture, CanRemoveColliderFromSimulatedObject)
  361. {
  362. RecordProperty("test_case_id", "C13048817");
  363. AZ::Data::AssetId actorAssetId("{5060227D-B6F4-422E-BF82-41AAC5F228A5}");
  364. m_actorAsset = TestActorAssets::CreateActorAssetAndRegister<SimpleJointChainActor>(actorAssetId, 5, "CanAddSimulatedObjectActor");
  365. Actor* actor = m_actorAsset->GetActor();
  366. EMStudio::GetMainWindow()->ApplicationModeChanged("SimulatedObjects");
  367. QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
  368. CreateSimulateObject("sim1");
  369. m_skeletonOutliner = static_cast<EMotionFX::SkeletonOutlinerPlugin*>(EMStudio::GetPluginManager()->FindActivePlugin(EMotionFX::SkeletonOutlinerPlugin::CLASS_ID));
  370. m_skeletonTreeView = m_skeletonOutliner->GetDockWidget()->findChild<ReselectingTreeView*>("EMFX.SkeletonOutlinerPlugin.SkeletonOutlinerTreeView");
  371. m_skeletonModel = m_skeletonTreeView->model();
  372. m_indexList.clear();
  373. m_skeletonTreeView->RecursiveGetAllChildren(m_skeletonModel->index(0, 0, m_skeletonModel->index(0, 0)), m_indexList);
  374. // Add colliders to two joints.
  375. AddCapsuleColliderToJointIndex(3);
  376. AddCapsuleColliderToJointIndex(4);
  377. const size_t numCollidersAfterAdd = PhysicsSetupUtils::CountColliders(actor, PhysicsSetup::SimulatedObjectCollider);
  378. EXPECT_EQ(numCollidersAfterAdd, 2);
  379. m_indexList.clear();
  380. m_skeletonTreeView->RecursiveGetAllChildren(m_skeletonModel->index(0, 0, m_skeletonModel->index(0, 0)), m_indexList);
  381. // Reselect joint 3 and pop up the context menu for it.
  382. m_skeletonTreeView->selectionModel()->clearSelection();
  383. SelectIndexes(m_indexList, m_skeletonTreeView, 3, 3);
  384. // Open the Right Click Context Menu
  385. const QRect rect = m_skeletonTreeView->visualRect(m_indexList[2]);
  386. EXPECT_TRUE(rect.isValid());
  387. BringUpContextMenu(m_skeletonTreeView, rect);
  388. const QList<QMenu*> contextMenus = m_skeletonOutliner->GetDockWidget()->findChildren<QMenu*>("EMFX.SkeletonOutlinerPlugin.ContextMenu");
  389. EXPECT_NE(contextMenus.size(), 0) << "Unable to find Skeketon Outliner context menu.";
  390. // There will be several existing menus, as the Qt event loop has not yet been run, so we need to find the latest and use that.
  391. QMenu* contextMenu = *(contextMenus.end() - 1);
  392. QAction* removeAction = contextMenu->findChild<QAction*>("EMFX.SimulatedObjectWidget.RemoveCollidersAction");
  393. ASSERT_TRUE(removeAction);
  394. removeAction->trigger();
  395. // Check that one of the colliders is now gone.
  396. const size_t numCollidersAfterFirstRemove = PhysicsSetupUtils::CountColliders(actor, PhysicsSetup::SimulatedObjectCollider);
  397. ASSERT_EQ(numCollidersAfterFirstRemove, numCollidersAfterAdd - 1) << "RemoveCollider action in Simulated Object Inspector failed.";
  398. // Now do the same thing using the Simulated Object Inspector context menu.
  399. const SimulatedObjectColliderWidget* simulatedObjectColliderWidget = GetSimulatedObjectColliderWidget();
  400. ASSERT_TRUE(simulatedObjectColliderWidget) << "SimulatedObjectColliderWidget not found.";
  401. // Select the second collider that was made earlier.
  402. m_skeletonTreeView->selectionModel()->clearSelection();
  403. SelectIndexes(m_indexList, m_skeletonTreeView, 4, 4);
  404. const ColliderContainerWidget* colliderContainerWidget = simulatedObjectColliderWidget->findChild< ColliderContainerWidget*>();
  405. ASSERT_TRUE(colliderContainerWidget) << "ColliderContainerWidget not found.";
  406. // Get the collider widget card from the container.
  407. const ColliderWidget* colliderWidget = colliderContainerWidget->findChild<ColliderWidget*>();
  408. ASSERT_TRUE(colliderWidget) << "ColliderWidget not found.";
  409. const AzQtComponents::CardHeader* cardHeader = colliderWidget->findChild<AzQtComponents::CardHeader*>();
  410. ASSERT_TRUE(cardHeader) << "ColliderWidget CardHeader not found.";
  411. const QFrame* frame = cardHeader->findChild<QFrame*>("Background");
  412. ASSERT_TRUE(frame) << "ColliderWidget CardHeader Background Frame not found.";
  413. QPushButton* contextMenubutton = frame->findChild< QPushButton*>("ContextMenu");
  414. ASSERT_TRUE(contextMenubutton) << "ColliderWidget ContextMenu not found.";
  415. // Pop up the context menu.
  416. QTest::mouseClick(contextMenubutton, Qt::LeftButton);
  417. // Find the delete collider button and press it.
  418. const QMenu* collilderWidgetContextMenu = colliderWidget->findChild<QMenu*>("EMFX.ColliderContainerWidget.ContextMenu");
  419. QAction* delAction = collilderWidgetContextMenu->findChild<QAction*>("EMFX.ColliderContainerWidget.DeleteColliderAction");
  420. delAction->trigger();
  421. // Check that we have the number of colliders we started we expect.
  422. const size_t numCollidersAfterSecondRemove = PhysicsSetupUtils::CountColliders(actor, PhysicsSetup::SimulatedObjectCollider);
  423. ASSERT_EQ(numCollidersAfterSecondRemove, numCollidersAfterAdd - 2);
  424. }
  425. } // namespace EMotionFX