SimulatedObjectWidget.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603
  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 <AzCore/std/algorithm.h>
  9. #include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
  10. #include <AzFramework/Entity/EntityDebugDisplayBus.h>
  11. #include <EMotionFX/CommandSystem/Source/SimulatedObjectCommands.h>
  12. #include <EMotionFX/Source/Actor.h>
  13. #include <EMotionFX/Source/ActorInstance.h>
  14. #include <EMotionFX/Source/ActorManager.h>
  15. #include <EMotionFX/Source/DebugDraw.h>
  16. #include <EMotionFX/Source/TransformData.h>
  17. #include <EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/EMStudioManager.h>
  18. #include <EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderOptions.h>
  19. #include <EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/ViewportPluginBus.h>
  20. #include <Editor/ColliderContainerWidget.h>
  21. #include <Editor/ColliderHelpers.h>
  22. #include <Editor/Plugins/SimulatedObject/SimulatedJointWidget.h>
  23. #include <Editor/Plugins/SimulatedObject/SimulatedObjectWidget.h>
  24. #include <Editor/Plugins/ColliderWidgets/SimulatedObjectColliderWidget.h>
  25. #include <Editor/ReselectingTreeView.h>
  26. #include <Editor/SimulatedObjectHelpers.h>
  27. #include <Editor/SkeletonModel.h>
  28. #include <Integration/Rendering/RenderActorSettings.h>
  29. #include <MCore/Source/AzCoreConversions.h>
  30. #include <QLabel>
  31. #include <QPushButton>
  32. #include <QTreeView>
  33. #include <QVBoxLayout>
  34. #include <QMessageBox>
  35. namespace EMotionFX
  36. {
  37. int SimulatedObjectWidget::s_jointLabelSpacing = 17;
  38. SimulatedObjectWidget::SimulatedObjectWidget()
  39. : EMStudio::DockWidgetPlugin()
  40. {
  41. m_actionManager = AZStd::make_unique<EMStudio::SimulatedObjectActionManager>();
  42. }
  43. SimulatedObjectWidget::~SimulatedObjectWidget()
  44. {
  45. for (MCore::Command::Callback* callback : m_commandCallbacks)
  46. {
  47. CommandSystem::GetCommandManager()->RemoveCommandCallback(callback, true);
  48. }
  49. m_commandCallbacks.clear();
  50. if (m_simulatedObjectInspectorDock)
  51. {
  52. EMStudio::GetMainWindow()->removeDockWidget(m_simulatedObjectInspectorDock);
  53. delete m_simulatedObjectInspectorDock;
  54. }
  55. SkeletonOutlinerNotificationBus::Handler::BusDisconnect();
  56. SimulatedObjectRequestBus::Handler::BusDisconnect();
  57. ActorEditorNotificationBus::Handler::BusDisconnect();
  58. }
  59. bool SimulatedObjectWidget::Init()
  60. {
  61. m_noSelectionWidget = new QLabel("Add a simulated object first, then add the joints you want to simulate to the object and customize the simulation settings.");
  62. m_noSelectionWidget->setWordWrap(true);
  63. m_simulatedObjectModel = AZStd::make_unique<SimulatedObjectModel>();
  64. m_treeView = new ReselectingTreeView();
  65. m_treeView->setObjectName("EMFX.SimulatedObjectWidget.TreeView");
  66. m_treeView->setModel(m_simulatedObjectModel.get());
  67. m_treeView->setSelectionModel(m_simulatedObjectModel->GetSelectionModel());
  68. m_treeView->setSelectionBehavior(QAbstractItemView::SelectionBehavior::SelectRows);
  69. m_treeView->setSelectionMode(QAbstractItemView::ExtendedSelection);
  70. m_treeView->setContextMenuPolicy(Qt::CustomContextMenu);
  71. m_treeView->setExpandsOnDoubleClick(true);
  72. m_treeView->expandAll();
  73. connect(m_treeView, &QTreeView::customContextMenuRequested, this, static_cast<void (SimulatedObjectWidget::*)(const QPoint&)>(&SimulatedObjectWidget::OnContextMenu));
  74. connect(m_simulatedObjectModel.get(), &QAbstractItemModel::modelReset, m_treeView, &QTreeView::expandAll);
  75. connect(m_simulatedObjectModel->GetSelectionModel(), &QItemSelectionModel::selectionChanged, this, [this]() {
  76. const QModelIndexList& selectedIndices = m_simulatedObjectModel->GetSelectionModel()->selectedRows();
  77. if (selectedIndices.empty())
  78. {
  79. EMStudio::GetManager()->SetSelectedJointIndices({});
  80. }
  81. else
  82. {
  83. AZStd::unordered_set<size_t> selectedJointIndices;
  84. for (const QModelIndex& index : selectedIndices)
  85. {
  86. const SimulatedJoint* joint = index.data(SimulatedObjectModel::ROLE_JOINT_PTR).value<SimulatedJoint*>();
  87. if (joint)
  88. {
  89. selectedJointIndices.emplace(joint->GetSkeletonJointIndex());
  90. }
  91. else
  92. {
  93. const SimulatedObject* object = index.data(SimulatedObjectModel::ROLE_OBJECT_PTR).value<SimulatedObject*>();
  94. if (object)
  95. {
  96. for (const auto& jointInObject : object->GetSimulatedJoints())
  97. {
  98. selectedJointIndices.emplace(jointInObject->GetSkeletonJointIndex());
  99. }
  100. }
  101. }
  102. }
  103. EMStudio::GetManager()->SetSelectedJointIndices(selectedJointIndices);
  104. }
  105. });
  106. m_addSimulatedObjectButton = new QPushButton("Add simulated object");
  107. m_addSimulatedObjectButton->setObjectName("addSimulatedObjectButton");
  108. connect(m_addSimulatedObjectButton, &QPushButton::clicked, this, [this]()
  109. {
  110. m_actionManager->OnAddNewObjectAndAddJoints(m_actor, /*selectedJoints=*/{}, /*addChildJoints=*/false, m_dock);
  111. });
  112. AZ::SerializeContext* serializeContext;
  113. AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
  114. m_selectionWidget = new QWidget();
  115. QVBoxLayout* selectionLayout = new QVBoxLayout(m_selectionWidget);
  116. selectionLayout->addWidget(m_treeView);
  117. m_mainWidget = new QWidget();
  118. QVBoxLayout* mainLayout = new QVBoxLayout(m_mainWidget);
  119. mainLayout->addWidget(m_addSimulatedObjectButton);
  120. // Add to simulated object button
  121. AddToSimulatedObjectButton* addObjectButtonn = new AddToSimulatedObjectButton("Add to simulated object", m_dock);
  122. selectionLayout->addWidget(addObjectButtonn);
  123. // Add collider button
  124. AddColliderButton* addColliderButton = new AddColliderButton("Add simulated object collider", m_dock,
  125. PhysicsSetup::ColliderConfigType::SimulatedObjectCollider,
  126. { azrtti_typeid<Physics::CapsuleShapeConfiguration>(),
  127. azrtti_typeid<Physics::SphereShapeConfiguration>() });
  128. addColliderButton->setObjectName("EMFX.SimulatedObjectColliderWidget.AddColliderButton");
  129. connect(addColliderButton, &AddColliderButton::AddCollider, this, &SimulatedObjectWidget::OnAddColliderByType);
  130. selectionLayout->addWidget(addColliderButton);
  131. m_instruction1 = new QLabel("To simulated the selected joint, add it to a Simulated Object by clicking on the \"Add to Simulated Object\" button above", m_dock);
  132. m_instruction1->setWordWrap(true);
  133. m_instruction2 = new QLabel("If you want the selected joint to collide against a Simulated Object, add a collider to the selected joint, and then set up the \"Collide with\" settings under the Simulated Object", m_dock);
  134. m_instruction2->setWordWrap(true);
  135. selectionLayout->addWidget(m_instruction1);
  136. selectionLayout->addWidget(m_instruction2);
  137. mainLayout->addWidget(m_noSelectionWidget);
  138. mainLayout->addWidget(m_selectionWidget, /*stretch=*/1);
  139. mainLayout->addStretch();
  140. m_dock->setWidget(m_mainWidget);
  141. m_simulatedObjectInspectorDock = new AzQtComponents::StyledDockWidget("Simulated Object Inspector", m_dock);
  142. m_simulatedObjectInspectorDock->setFeatures(QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable);
  143. m_simulatedObjectInspectorDock->setObjectName("EMFX.SimulatedObjectWidget.SimulatedObjectInspectorDock");
  144. m_simulatedJointWidget = new SimulatedJointWidget(this);
  145. m_simulatedObjectInspectorDock->setWidget(m_simulatedJointWidget);
  146. QMainWindow* mainWindow = EMStudio::GetMainWindow();
  147. mainWindow->addDockWidget(Qt::RightDockWidgetArea, m_simulatedObjectInspectorDock);
  148. // Check if there is already an actor selected.
  149. ActorEditorRequestBus::BroadcastResult(m_actorInstance, &ActorEditorRequests::GetSelectedActorInstance);
  150. if (m_actorInstance)
  151. {
  152. // Only need to set the actor instance on the model, as this function also set the actor.
  153. m_simulatedObjectModel->SetActorInstance(m_actorInstance);
  154. m_actor = m_actorInstance->GetActor();
  155. }
  156. else
  157. {
  158. ActorEditorRequestBus::BroadcastResult(m_actor, &ActorEditorRequests::GetSelectedActor);
  159. m_simulatedObjectModel->SetActor(m_actor);
  160. }
  161. Reinit();
  162. // Register command callback
  163. m_commandCallbacks.emplace_back(new DataChangedCallback(/*executePreUndo*/ false));
  164. CommandSystem::GetCommandManager()->RegisterCommandCallback(CommandAddSimulatedObject::s_commandName, m_commandCallbacks.back());
  165. CommandSystem::GetCommandManager()->RegisterCommandCallback(CommandAddSimulatedJoints::s_commandName, m_commandCallbacks.back());
  166. CommandSystem::GetCommandManager()->RegisterCommandCallback(CommandRemoveSimulatedObject::s_commandName, m_commandCallbacks.back());
  167. CommandSystem::GetCommandManager()->RegisterCommandCallback(CommandRemoveSimulatedJoints::s_commandName, m_commandCallbacks.back());
  168. m_commandCallbacks.emplace_back(new AddSimulatedObjectCallback(/*executePreUndo*/ false));
  169. CommandSystem::GetCommandManager()->RegisterCommandCallback(CommandAddSimulatedObject::s_commandName, m_commandCallbacks.back());
  170. m_commandCallbacks.emplace_back(new AddSimulatedJointsCallback(/*executePreUndo*/ false));
  171. CommandSystem::GetCommandManager()->RegisterCommandCallback(CommandAddSimulatedJoints::s_commandName, m_commandCallbacks.back());
  172. // Buses
  173. SkeletonOutlinerNotificationBus::Handler::BusConnect();
  174. SimulatedObjectRequestBus::Handler::BusConnect();
  175. ActorEditorNotificationBus::Handler::BusConnect();
  176. return true;
  177. }
  178. void SimulatedObjectWidget::ActorSelectionChanged(Actor* actor)
  179. {
  180. m_actor = actor;
  181. m_simulatedObjectModel->SetActor(actor);
  182. Reinit();
  183. }
  184. void SimulatedObjectWidget::ActorInstanceSelectionChanged(EMotionFX::ActorInstance* actorInstance)
  185. {
  186. m_actorInstance = actorInstance;
  187. m_actor = nullptr;
  188. if (m_actorInstance)
  189. {
  190. m_actor = m_actorInstance->GetActor();
  191. }
  192. m_simulatedObjectModel->SetActorInstance(actorInstance);
  193. Reinit();
  194. }
  195. void SimulatedObjectWidget::Reinit()
  196. {
  197. const bool showSelectionWidget = m_actor ? (m_actor->GetSimulatedObjectSetup()->GetNumSimulatedObjects() != 0) : false;
  198. m_noSelectionWidget->setVisible(!showSelectionWidget);
  199. m_selectionWidget->setVisible(showSelectionWidget);
  200. m_simulatedJointWidget->UpdateDetailsView(QItemSelection(), QItemSelection());
  201. m_addSimulatedObjectButton->setVisible(m_actorInstance != nullptr);
  202. }
  203. QModelIndexList SimulatedObjectWidget::GetSelectedModelIndices() const
  204. {
  205. QModelIndexList selectedModelIndices;
  206. SkeletonModel* skeletonModel = nullptr;
  207. SkeletonOutlinerRequestBus::BroadcastResult(skeletonModel, &SkeletonOutlinerRequests::GetModel);
  208. if (skeletonModel)
  209. {
  210. selectedModelIndices = skeletonModel->GetSelectionModel().selectedRows();
  211. }
  212. return selectedModelIndices;
  213. }
  214. SimulatedObjectModel* SimulatedObjectWidget::GetSimulatedObjectModel() const
  215. {
  216. return m_simulatedObjectModel.get();
  217. }
  218. SimulatedJointWidget* SimulatedObjectWidget::GetSimulatedJointWidget() const
  219. {
  220. return m_simulatedJointWidget;
  221. }
  222. void SimulatedObjectWidget::ScrollTo(const QModelIndex& index)
  223. {
  224. m_treeView->scrollTo(index, QAbstractItemView::ScrollHint::PositionAtCenter);
  225. }
  226. // Called when right-clicked the simulated object widget.
  227. void SimulatedObjectWidget::OnContextMenu(const QPoint& position)
  228. {
  229. const QModelIndexList& selectedIndices = m_treeView->selectionModel()->selectedRows(0);
  230. const QModelIndex currentIndex = m_treeView->currentIndex();
  231. if (!currentIndex.isValid())
  232. {
  233. return;
  234. }
  235. QMenu* contextMenu = new QMenu(m_mainWidget);
  236. contextMenu->setObjectName("EMFX.SimulatedObjectWidget.ContextMenu");
  237. const bool isJoint = currentIndex.data(SimulatedObjectModel::ROLE_JOINT_BOOL).toBool();
  238. if (isJoint)
  239. {
  240. if (selectedIndices.count() == 1)
  241. {
  242. QAction* removeJoint = contextMenu->addAction("Remove joint");
  243. connect(removeJoint, &QAction::triggered, [this, currentIndex]() { OnRemoveSimulatedJoint(currentIndex, false); });
  244. QAction* removeJointAndChildren = contextMenu->addAction("Remove joint and children");
  245. connect(removeJointAndChildren, &QAction::triggered, [this, currentIndex]() { OnRemoveSimulatedJoint(currentIndex, true); });
  246. }
  247. else
  248. {
  249. QAction* removeJoints = contextMenu->addAction("Remove joints");
  250. connect(removeJoints, &QAction::triggered, [this, selectedIndices]() { OnRemoveSimulatedJoints(selectedIndices); });
  251. }
  252. }
  253. else
  254. {
  255. QAction* removeObject = contextMenu->addAction("Remove object");
  256. connect(removeObject, &QAction::triggered, [this, currentIndex]() { OnRemoveSimulatedObject(currentIndex); });
  257. }
  258. if (!contextMenu->isEmpty())
  259. {
  260. contextMenu->popup(m_treeView->mapToGlobal(position));
  261. }
  262. connect(contextMenu, &QMenu::triggered, contextMenu, &QMenu::deleteLater);
  263. }
  264. void SimulatedObjectWidget::OnRemoveSimulatedObject(const QModelIndex& objectIndex)
  265. {
  266. SimulatedObjectHelpers::RemoveSimulatedObject(objectIndex);
  267. }
  268. void SimulatedObjectWidget::OnRemoveSimulatedJoint(const QModelIndex& jointIndex, bool removeChildren)
  269. {
  270. SimulatedObjectHelpers::RemoveSimulatedJoint(jointIndex, removeChildren);
  271. }
  272. void SimulatedObjectWidget::OnRemoveSimulatedJoints(const QModelIndexList& jointIndices)
  273. {
  274. // We don't give the option to remove children when multiple joints are selected.
  275. SimulatedObjectHelpers::RemoveSimulatedJoints(jointIndices, false);
  276. }
  277. void SimulatedObjectWidget::OnAddCollider()
  278. {
  279. AZ::Outcome<QModelIndexList> selectedRowIndicesOutcome;
  280. SkeletonOutlinerRequestBus::BroadcastResult(selectedRowIndicesOutcome, &SkeletonOutlinerRequests::GetSelectedRowIndices);
  281. if (!selectedRowIndicesOutcome.IsSuccess())
  282. {
  283. return;
  284. }
  285. const QModelIndexList& selectedRowIndices = selectedRowIndicesOutcome.GetValue();
  286. if (selectedRowIndices.empty())
  287. {
  288. return;
  289. }
  290. QAction* action = static_cast<QAction*>(sender());
  291. const QByteArray typeString = action->property("typeId").toString().toUtf8();
  292. const AZ::TypeId& colliderType = AZ::TypeId::CreateString(typeString.data(), typeString.size());
  293. ColliderHelpers::AddCollider(selectedRowIndices, PhysicsSetup::SimulatedObjectCollider, colliderType);
  294. }
  295. void SimulatedObjectWidget::OnAddColliderByType(const AZ::TypeId& colliderType)
  296. {
  297. ColliderHelpers::AddCollider(GetSelectedModelIndices(), PhysicsSetup::SimulatedObjectCollider, colliderType);
  298. }
  299. void SimulatedObjectWidget::OnClearColliders()
  300. {
  301. AZ::Outcome<QModelIndexList> selectedRowIndicesOutcome;
  302. SkeletonOutlinerRequestBus::BroadcastResult(selectedRowIndicesOutcome, &SkeletonOutlinerRequests::GetSelectedRowIndices);
  303. if (!selectedRowIndicesOutcome.IsSuccess())
  304. {
  305. return;
  306. }
  307. const QModelIndexList& selectedRowIndices = selectedRowIndicesOutcome.GetValue();
  308. if (selectedRowIndices.empty())
  309. {
  310. return;
  311. }
  312. ColliderHelpers::ClearColliders(selectedRowIndices, PhysicsSetup::SimulatedObjectCollider);
  313. }
  314. // Called when right-clicked the skeleton outliner widget.
  315. void SimulatedObjectWidget::OnContextMenu(QMenu* menu, const QModelIndexList& selectedRowIndices)
  316. {
  317. if (selectedRowIndices.empty())
  318. {
  319. return;
  320. }
  321. const Actor* actor = selectedRowIndices[0].data(SkeletonModel::ROLE_ACTOR_POINTER).value<Actor*>();
  322. const SimulatedObjectSetup* simulatedObjectSetup = actor->GetSimulatedObjectSetup().get();
  323. if (!simulatedObjectSetup)
  324. {
  325. AZ_Assert(false, "Expected a simulated object setup on the actor.");
  326. return;
  327. }
  328. AZStd::unordered_set<const SimulatedObject*> addToCandidates;
  329. for (const QModelIndex& index : selectedRowIndices)
  330. {
  331. const Node* joint = index.data(SkeletonModel::ROLE_POINTER).value<Node*>();
  332. for (const SimulatedObject* object : simulatedObjectSetup->GetSimulatedObjects())
  333. {
  334. if (!object->FindSimulatedJointBySkeletonJointIndex(joint->GetNodeIndex()))
  335. {
  336. addToCandidates.emplace(object);
  337. }
  338. }
  339. }
  340. QMenu* addToSimulatedObjectMenu = menu->addMenu("Add to simulated object");
  341. if (!addToCandidates.empty())
  342. {
  343. for (const SimulatedObject* object : addToCandidates)
  344. {
  345. QAction* openItem = addToSimulatedObjectMenu->addAction(object->GetName().c_str());
  346. connect(openItem, &QAction::triggered, [this, selectedRowIndices, simulatedObjectSetup, object]() {
  347. const bool addChildren = (QMessageBox::question(this->GetDockWidget(),
  348. "Add children of joints?", "Add all children of selected joints to the simulated object?") == QMessageBox::Yes);
  349. SimulatedObjectHelpers::AddSimulatedJoints(selectedRowIndices, simulatedObjectSetup->FindSimulatedObjectIndex(object).GetValue(), addChildren);
  350. });
  351. }
  352. addToSimulatedObjectMenu->addSeparator();
  353. }
  354. connect(addToSimulatedObjectMenu->addAction("New simulated object..."), &QAction::triggered, this, [this, selectedRowIndices]() {
  355. const bool addChildren = (QMessageBox::question(this->GetDockWidget(),
  356. "Add children of joints?", "Add all children of selected joints to the simulated object?") == QMessageBox::Yes);
  357. m_actionManager->OnAddNewObjectAndAddJoints(m_actor, selectedRowIndices, addChildren, m_dock);
  358. });
  359. menu->addSeparator();
  360. const AZStd::shared_ptr<PhysicsSetup>& physicsSetup = actor->GetPhysicsSetup();
  361. if (!physicsSetup)
  362. {
  363. return;
  364. }
  365. if (ColliderHelpers::AreCollidersReflected())
  366. {
  367. if (selectedRowIndices.count() > 0)
  368. {
  369. QMenu* addColliderMenu = menu->addMenu("Add collider");
  370. QAction* addCapsuleAction = addColliderMenu->addAction("Capsule");
  371. addCapsuleAction->setProperty("typeId", azrtti_typeid<Physics::CapsuleShapeConfiguration>().ToString<AZStd::string>().c_str());
  372. connect(addCapsuleAction, &QAction::triggered, this, &SimulatedObjectWidget::OnAddCollider);
  373. QAction* addSphereAction = addColliderMenu->addAction("Sphere");
  374. addSphereAction->setProperty("typeId", azrtti_typeid<Physics::SphereShapeConfiguration>().ToString<AZStd::string>().c_str());
  375. connect(addSphereAction, &QAction::triggered, this, &SimulatedObjectWidget::OnAddCollider);
  376. ColliderHelpers::AddCopyFromMenu(this, menu, PhysicsSetup::ColliderConfigType::SimulatedObjectCollider, selectedRowIndices);
  377. }
  378. const bool anySelectedJointHasCollider = AZStd::any_of(selectedRowIndices.begin(), selectedRowIndices.end(), [](const QModelIndex& modelIndex)
  379. {
  380. return modelIndex.data(SkeletonModel::ROLE_SIMULATED_OBJECT_COLLIDER).toBool();
  381. });
  382. if (anySelectedJointHasCollider)
  383. {
  384. QAction* removeCollidersAction = menu->addAction("Remove colliders");
  385. removeCollidersAction->setObjectName("EMFX.SimulatedObjectWidget.RemoveCollidersAction");
  386. connect(removeCollidersAction, &QAction::triggered, this, &SimulatedObjectWidget::OnClearColliders);
  387. }
  388. menu->addSeparator();
  389. }
  390. }
  391. void SimulatedObjectWidget::UpdateWidget()
  392. {
  393. Reinit();
  394. }
  395. bool SimulatedObjectWidget::DataChangedCallback::Execute(MCore::Command* command, const MCore::CommandLine& commandLine)
  396. {
  397. AZ_UNUSED(command);
  398. AZ_UNUSED(commandLine);
  399. EMotionFX::SimulatedObjectRequestBus::Broadcast(&EMotionFX::SimulatedObjectRequests::UpdateWidget);
  400. return true;
  401. }
  402. bool SimulatedObjectWidget::DataChangedCallback::Undo(MCore::Command* command, const MCore::CommandLine& commandLine)
  403. {
  404. AZ_UNUSED(command);
  405. AZ_UNUSED(commandLine);
  406. EMotionFX::SimulatedObjectRequestBus::Broadcast(&EMotionFX::SimulatedObjectRequests::UpdateWidget);
  407. return true;
  408. }
  409. bool SimulatedObjectWidget::AddSimulatedObjectCallback::Execute(MCore::Command* command, [[maybe_unused]] const MCore::CommandLine& commandLine)
  410. {
  411. CommandAddSimulatedObject* addSimulatedObjectCommand = static_cast<CommandAddSimulatedObject*>(command);
  412. const size_t objectIndex = addSimulatedObjectCommand->GetObjectIndex();
  413. SimulatedObjectWidget* simulatedObjectPlugin = static_cast<SimulatedObjectWidget*>(EMStudio::GetPluginManager()->FindActivePlugin(SimulatedObjectWidget::CLASS_ID));
  414. if (simulatedObjectPlugin)
  415. {
  416. const QModelIndex modelIndex = simulatedObjectPlugin->GetSimulatedObjectModel()->GetModelIndexByObjectIndex(objectIndex);
  417. simulatedObjectPlugin->GetSimulatedObjectModel()->GetSelectionModel()->select(modelIndex, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
  418. simulatedObjectPlugin->ScrollTo(modelIndex);
  419. }
  420. return true;
  421. }
  422. bool SimulatedObjectWidget::AddSimulatedObjectCallback::Undo(MCore::Command* command, const MCore::CommandLine& commandLine)
  423. {
  424. AZ_UNUSED(command);
  425. AZ_UNUSED(commandLine);
  426. return true;
  427. }
  428. bool SimulatedObjectWidget::AddSimulatedJointsCallback::Execute(MCore::Command* command, [[maybe_unused]] const MCore::CommandLine& commandLine)
  429. {
  430. CommandAddSimulatedJoints* addSimulatedJointsCommand = static_cast<CommandAddSimulatedJoints*>(command);
  431. const size_t objectIndex = addSimulatedJointsCommand->GetObjectIndex();
  432. const AZStd::vector<size_t>& jointIndices = addSimulatedJointsCommand->GetJointIndices();
  433. SimulatedObjectWidget* simulatedObjectPlugin = static_cast<SimulatedObjectWidget*>(EMStudio::GetPluginManager()->FindActivePlugin(SimulatedObjectWidget::CLASS_ID));
  434. if (simulatedObjectPlugin)
  435. {
  436. QItemSelection selection;
  437. simulatedObjectPlugin->GetSimulatedObjectModel()->AddJointsToSelection(selection, objectIndex, jointIndices);
  438. simulatedObjectPlugin->GetSimulatedObjectModel()->GetSelectionModel()->select(selection, QItemSelectionModel::Current | QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
  439. if (!selection.empty())
  440. {
  441. const QModelIndexList list = selection.indexes();
  442. simulatedObjectPlugin->ScrollTo(list[0]);
  443. }
  444. }
  445. return true;
  446. }
  447. bool SimulatedObjectWidget::AddSimulatedJointsCallback::Undo(MCore::Command* command, const MCore::CommandLine& commandLine)
  448. {
  449. AZ_UNUSED(command);
  450. AZ_UNUSED(commandLine);
  451. return true;
  452. }
  453. void SimulatedObjectWidget::Render(EMotionFX::ActorRenderFlags renderFlags)
  454. {
  455. if (!m_actor || !m_actorInstance)
  456. {
  457. return;
  458. }
  459. const AZStd::unordered_set<size_t>& selectedJointIndices = EMStudio::GetManager()->GetSelectedJointIndices();
  460. if (AZ::RHI::CheckBitsAny(renderFlags, EMotionFX::ActorRenderFlags::SimulatedJoints) && !selectedJointIndices.empty())
  461. {
  462. // Render the joint radius.
  463. const size_t actorInstanceCount = GetActorManager().GetNumActorInstances();
  464. for (size_t actorInstanceIndex = 0; actorInstanceIndex < actorInstanceCount; ++actorInstanceIndex)
  465. {
  466. ActorInstance* actorInstance = GetActorManager().GetActorInstance(actorInstanceIndex);
  467. const Actor* actor = actorInstance->GetActor();
  468. const SimulatedObjectSetup* setup = actor->GetSimulatedObjectSetup().get();
  469. if (!setup)
  470. {
  471. AZ_Assert(false, "Expected a simulated object setup on the actor instance.");
  472. return;
  473. }
  474. const size_t objectCount = setup->GetNumSimulatedObjects();
  475. for (size_t objectIndex = 0; objectIndex < objectCount; ++objectIndex)
  476. {
  477. const SimulatedObject* object = setup->GetSimulatedObject(objectIndex);
  478. const size_t simulatedJointCount = object->GetNumSimulatedJoints();
  479. for (size_t simulatedJointIndex = 0; simulatedJointIndex < simulatedJointCount; ++simulatedJointIndex)
  480. {
  481. const SimulatedJoint* simulatedJoint = object->GetSimulatedJoint(simulatedJointIndex);
  482. const size_t skeletonJointIndex = simulatedJoint->GetSkeletonJointIndex();
  483. if (selectedJointIndices.find(skeletonJointIndex) != selectedJointIndices.end())
  484. {
  485. RenderJointRadius(simulatedJoint, actorInstance, AZ::Color(1.0f, 0.0f, 1.0f, 1.0f));
  486. }
  487. }
  488. }
  489. }
  490. }
  491. }
  492. void SimulatedObjectWidget::RenderJointRadius(const SimulatedJoint* joint, ActorInstance* actorInstance, const AZ::Color& color)
  493. {
  494. #ifndef EMFX_SCALE_DISABLED
  495. const float scale = actorInstance->GetWorldSpaceTransform().m_scale.GetX();
  496. #else
  497. const float scale = 1.0f;
  498. #endif
  499. const float radius = joint->GetCollisionRadius() * scale;
  500. if (radius <= AZ::Constants::FloatEpsilon)
  501. {
  502. return;
  503. }
  504. AZ_Assert(joint->GetSkeletonJointIndex() != InvalidIndex, "Expected skeletal joint index to be valid.");
  505. const EMotionFX::Transform jointTransform =
  506. actorInstance->GetTransformData()->GetCurrentPose()->GetWorldSpaceTransform(joint->GetSkeletonJointIndex());
  507. AZ::s32 viewportId = -1;
  508. EMStudio::ViewportPluginRequestBus::BroadcastResult(viewportId, &EMStudio::ViewportPluginRequestBus::Events::GetViewportId);
  509. AzFramework::DebugDisplayRequestBus::BusPtr debugDisplayBus;
  510. AzFramework::DebugDisplayRequestBus::Bind(debugDisplayBus, viewportId);
  511. AzFramework::DebugDisplayRequests* debugDisplay = nullptr;
  512. debugDisplay = AzFramework::DebugDisplayRequestBus::FindFirstHandler(debugDisplayBus);
  513. if (!debugDisplay)
  514. {
  515. return;
  516. }
  517. debugDisplay->SetColor(color);
  518. debugDisplay->DrawWireSphere(jointTransform.m_position, radius);
  519. }
  520. } // namespace EMotionFX