EditorWhiteBoxComponentMode.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638
  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 "EditorWhiteBoxComponentMode.h"
  9. #include "SubComponentModes/EditorWhiteBoxDefaultMode.h"
  10. #include "SubComponentModes/EditorWhiteBoxEdgeRestoreMode.h"
  11. #include "SubComponentModes/EditorWhiteBoxTransformMode.h"
  12. #include "Viewport/WhiteBoxViewportConstants.h"
  13. #include <AzCore/Component/TransformBus.h>
  14. #include <AzCore/Settings/SettingsRegistry.h>
  15. #include <AzCore/std/sort.h>
  16. #include <AzToolsFramework/ActionManager/Action/ActionManagerInterface.h>
  17. #include <AzToolsFramework/ActionManager/Menu/MenuManagerInterface.h>
  18. #include <AzToolsFramework/ActionManager/HotKey/HotKeyManagerInterface.h>
  19. #include <AzToolsFramework/Editor/ActionManagerIdentifiers/EditorContextIdentifiers.h>
  20. #include <AzToolsFramework/Manipulators/ManipulatorSnapping.h>
  21. #include <AzToolsFramework/Manipulators/ManipulatorView.h>
  22. #include <AzToolsFramework/Maths/TransformUtils.h>
  23. #include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
  24. #include <QApplication> // required for querying modifier keys
  25. #include <QTimer>
  26. #include <QVBoxLayout>
  27. #include <WhiteBox/EditorWhiteBoxComponentBus.h>
  28. namespace WhiteBox
  29. {
  30. constexpr AZStd::string_view WhiteBoxTransformFeature = "/O3DE/Preferences/WhiteBox/TransformFeature";
  31. constexpr AZStd::string_view WhiteBoxDefaultSubModeIdentifier = "o3de.context.mode.whiteBox.default";
  32. constexpr AZStd::string_view WhiteBoxEdgeRestoreSubModeIdentifier = "o3de.context.mode.whiteBox.edgeRestore";
  33. constexpr AZStd::string_view WhiteBoxTransformSubModeIdentifier = "o3de.context.mode.whiteBox.transform";
  34. AZ_CLASS_ALLOCATOR_IMPL(EditorWhiteBoxComponentMode, AZ::SystemAllocator)
  35. static void SetViewportUiClusterActiveButton(
  36. AzToolsFramework::ViewportUi::ClusterId clusterId, AzToolsFramework::ViewportUi::ButtonId buttonId)
  37. {
  38. AzToolsFramework::ViewportUi::ViewportUiRequestBus::Event(
  39. AzToolsFramework::ViewportUi::DefaultViewportId,
  40. &AzToolsFramework::ViewportUi::ViewportUiRequestBus::Events::SetClusterActiveButton, clusterId, buttonId);
  41. AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::Broadcast(
  42. &AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::RefreshActions);
  43. }
  44. // helper function to return what modifier keys move us to restore mode
  45. static bool RestoreModifier(AzToolsFramework::ViewportInteraction::KeyboardModifiers modifiers)
  46. {
  47. return modifiers.Shift() && modifiers.Ctrl();
  48. }
  49. // helper function to return what type of edge selection mode we're in
  50. static EdgeSelectionType DecideEdgeSelectionMode(const SubMode subMode)
  51. {
  52. return subMode == SubMode::EdgeRestore ? EdgeSelectionType::All : EdgeSelectionType::Polygon;
  53. }
  54. EditorWhiteBoxComponentMode::EditorWhiteBoxComponentMode(
  55. const AZ::EntityComponentIdPair& entityComponentIdPair, const AZ::Uuid componentType)
  56. : EditorBaseComponentMode(entityComponentIdPair, componentType)
  57. , m_worldFromLocal(AZ::Transform::Identity())
  58. {
  59. AzFramework::EntityDebugDisplayEventBus::Handler::BusConnect(entityComponentIdPair.GetEntityId());
  60. EditorWhiteBoxComponentModeRequestBus::Handler::BusConnect(entityComponentIdPair);
  61. AZ::TransformNotificationBus::Handler::BusConnect(entityComponentIdPair.GetEntityId());
  62. EditorWhiteBoxComponentNotificationBus::Handler::BusConnect(entityComponentIdPair);
  63. // default behavior for querying modifier keys (ask the QApplication)
  64. m_keyboardModifierQueryFn = []()
  65. {
  66. return AzToolsFramework::ViewportInteraction::QueryKeyboardModifiers();
  67. };
  68. m_worldFromLocal = AzToolsFramework::WorldFromLocalWithUniformScale(entityComponentIdPair.GetEntityId());
  69. CreateSubModeSelectionCluster();
  70. // start with DefaultMode
  71. EnterDefaultMode();
  72. }
  73. EditorWhiteBoxComponentMode::~EditorWhiteBoxComponentMode()
  74. {
  75. RemoveSubModeSelectionCluster();
  76. EditorWhiteBoxComponentNotificationBus::Handler::BusDisconnect();
  77. AZ::TransformNotificationBus::Handler::BusDisconnect();
  78. EditorWhiteBoxComponentModeRequestBus::Handler::BusDisconnect();
  79. AzFramework::EntityDebugDisplayEventBus::Handler::BusDisconnect();
  80. }
  81. void EditorWhiteBoxComponentMode::Reflect(AZ::ReflectContext* context)
  82. {
  83. AzToolsFramework::ComponentModeFramework::ReflectEditorBaseComponentModeDescendant<EditorWhiteBoxComponentMode>(context);
  84. }
  85. void EditorWhiteBoxComponentMode::RegisterActionContextModes()
  86. {
  87. auto actionManagerInterface = AZ::Interface<AzToolsFramework::ActionManagerInterface>::Get();
  88. AZ_Assert(actionManagerInterface, "EditorWhiteBoxComponentMode - could not get ActionManagerInterface on RegisterActionContextModes.");
  89. actionManagerInterface->RegisterActionContextMode(EditorIdentifiers::MainWindowActionContextIdentifier, WhiteBoxDefaultSubModeIdentifier);
  90. actionManagerInterface->RegisterActionContextMode(EditorIdentifiers::MainWindowActionContextIdentifier, WhiteBoxEdgeRestoreSubModeIdentifier);
  91. actionManagerInterface->RegisterActionContextMode(EditorIdentifiers::MainWindowActionContextIdentifier, WhiteBoxTransformSubModeIdentifier);
  92. }
  93. void EditorWhiteBoxComponentMode::RegisterActionUpdaters()
  94. {
  95. DefaultMode::RegisterActionUpdaters();
  96. EdgeRestoreMode::RegisterActionUpdaters();
  97. TransformMode::RegisterActionUpdaters();
  98. }
  99. void EditorWhiteBoxComponentMode::RegisterActions()
  100. {
  101. DefaultMode::RegisterActions();
  102. EdgeRestoreMode::RegisterActions();
  103. TransformMode::RegisterActions();
  104. }
  105. void EditorWhiteBoxComponentMode::BindActionsToModes()
  106. {
  107. DefaultMode::BindActionsToModes(WhiteBoxDefaultSubModeIdentifier);
  108. EdgeRestoreMode::BindActionsToModes(WhiteBoxEdgeRestoreSubModeIdentifier);
  109. TransformMode::BindActionsToModes(WhiteBoxTransformSubModeIdentifier);
  110. }
  111. void EditorWhiteBoxComponentMode::BindActionsToMenus()
  112. {
  113. DefaultMode::BindActionsToMenus();
  114. EdgeRestoreMode::BindActionsToMenus();
  115. TransformMode::BindActionsToMenus();
  116. }
  117. void EditorWhiteBoxComponentMode::Refresh()
  118. {
  119. MarkWhiteBoxIntersectionDataDirty();
  120. AZStd::visit(
  121. [](auto& mode)
  122. {
  123. mode->Refresh();
  124. },
  125. m_modes);
  126. AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::Broadcast(
  127. &AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::RefreshActions);
  128. }
  129. static AZStd::optional<VertexIntersection> FindClosestVertexIntersection(
  130. const GeometryIntersectionData& whiteBoxIntersectionData, const AZ::Vector3& localRayOrigin,
  131. const AZ::Vector3& localRayDirection, const AZ::Transform& worldFromLocal,
  132. const AzFramework::CameraState& cameraState)
  133. {
  134. VertexIntersection vertexIntersection;
  135. const float scaleRecip = AzToolsFramework::ScaleReciprocal(worldFromLocal);
  136. // find the closest vertex bound
  137. for (const auto& vertexBound : whiteBoxIntersectionData.m_vertexBounds)
  138. {
  139. const AZ::Vector3 worldCenter = worldFromLocal.TransformPoint(vertexBound.m_bound.m_center);
  140. const float screenRadius = vertexBound.m_bound.m_radius *
  141. AzToolsFramework::CalculateScreenToWorldMultiplier(worldCenter, cameraState) * scaleRecip;
  142. float vertexDistance = std::numeric_limits<float>::max();
  143. const bool intersection = IntersectRayVertex(
  144. vertexBound.m_bound, screenRadius, localRayOrigin, localRayDirection, vertexDistance);
  145. if (intersection && vertexDistance < vertexIntersection.m_intersection.m_closestDistance)
  146. {
  147. vertexIntersection.m_closestVertexWithHandle = vertexBound;
  148. vertexIntersection.m_intersection.m_closestDistance = vertexDistance;
  149. }
  150. }
  151. if (vertexIntersection.m_intersection.m_closestDistance < std::numeric_limits<float>::max())
  152. {
  153. vertexIntersection.m_intersection.m_localIntersectionPoint =
  154. localRayOrigin + localRayDirection * vertexIntersection.m_intersection.m_closestDistance;
  155. return vertexIntersection;
  156. }
  157. else
  158. {
  159. return AZStd::optional<VertexIntersection>{};
  160. }
  161. }
  162. static AZStd::optional<EdgeIntersection> FindClosestEdgeIntersection(
  163. const GeometryIntersectionData& whiteBoxIntersectionData, const AZ::Vector3& localRayOrigin,
  164. const AZ::Vector3& localRayDirection, const AZ::Transform& worldFromLocal,
  165. const AzFramework::CameraState& cameraState)
  166. {
  167. EdgeIntersection edgeIntersection;
  168. const float scaleRecip = AzToolsFramework::ScaleReciprocal(worldFromLocal);
  169. // find the closest edge bound
  170. for (const auto& edgeBound : whiteBoxIntersectionData.m_edgeBounds)
  171. {
  172. // degenerate edges cause false positives in the intersection test
  173. if (edgeBound.m_bound.m_start.IsClose(edgeBound.m_bound.m_end))
  174. {
  175. continue;
  176. }
  177. const AZ::Vector3 localMidpoint = (edgeBound.m_bound.m_end + edgeBound.m_bound.m_start) * 0.5f;
  178. const AZ::Vector3 worldMidpoint = worldFromLocal.TransformPoint(localMidpoint);
  179. const float screenRadius = edgeBound.m_bound.m_radius *
  180. AzToolsFramework::CalculateScreenToWorldMultiplier(worldMidpoint, cameraState) * scaleRecip;
  181. float edgeDistance = std::numeric_limits<float>::max();
  182. const bool intersection =
  183. IntersectRayEdge(edgeBound.m_bound, screenRadius, localRayOrigin, localRayDirection, edgeDistance);
  184. if (intersection && edgeDistance < edgeIntersection.m_intersection.m_closestDistance)
  185. {
  186. edgeIntersection.m_closestEdgeWithHandle = edgeBound;
  187. edgeIntersection.m_intersection.m_closestDistance = edgeDistance;
  188. }
  189. }
  190. if (edgeIntersection.m_intersection.m_closestDistance < std::numeric_limits<float>::max())
  191. {
  192. // calculate closest intersection point
  193. edgeIntersection.m_intersection.m_localIntersectionPoint =
  194. localRayOrigin + localRayDirection * edgeIntersection.m_intersection.m_closestDistance;
  195. return edgeIntersection;
  196. }
  197. else
  198. {
  199. return AZStd::optional<EdgeIntersection>{};
  200. }
  201. }
  202. static AZStd::optional<PolygonIntersection> FindClosestPolygonIntersection(
  203. const GeometryIntersectionData& whiteBoxIntersectionData, const AZ::Vector3& localRayOrigin,
  204. const AZ::Vector3& localRayDirection)
  205. {
  206. PolygonIntersection polygonIntersection;
  207. // find closest polygon bound
  208. for (const auto& polygonBound : whiteBoxIntersectionData.m_polygonBounds)
  209. {
  210. int64_t pickedTriangleIndex;
  211. float polygonDistance = std::numeric_limits<float>::max();
  212. const bool intersection = IntersectRayPolygon(
  213. polygonBound.m_bound, localRayOrigin, localRayDirection, polygonDistance, pickedTriangleIndex);
  214. if (intersection && polygonDistance < polygonIntersection.m_intersection.m_closestDistance)
  215. {
  216. polygonIntersection.m_pickedFaceHandle = polygonBound.m_handle.m_faceHandles[pickedTriangleIndex];
  217. polygonIntersection.m_closestPolygonWithHandle = polygonBound;
  218. polygonIntersection.m_intersection.m_closestDistance = polygonDistance;
  219. polygonIntersection.m_intersection.m_localIntersectionPoint =
  220. localRayOrigin + localRayDirection * polygonDistance;
  221. }
  222. }
  223. return polygonIntersection.m_intersection.m_closestDistance < std::numeric_limits<float>::max()
  224. ? polygonIntersection
  225. : AZStd::optional<PolygonIntersection>{};
  226. }
  227. bool EditorWhiteBoxComponentMode::HandleMouseInteraction(
  228. const AzToolsFramework::ViewportInteraction::MouseInteractionEvent& mouseInteraction)
  229. {
  230. AZ_PROFILE_FUNCTION(AzToolsFramework);
  231. WhiteBoxMesh* whiteBox = nullptr;
  232. EditorWhiteBoxComponentRequestBus::EventResult(
  233. whiteBox, GetEntityComponentIdPair(), &EditorWhiteBoxComponentRequests::GetWhiteBoxMesh);
  234. // generate mesh to query if it needs to be rebuilt
  235. if (!m_intersectionAndRenderData.has_value())
  236. {
  237. RecalculateWhiteBoxIntersectionData(DecideEdgeSelectionMode(m_currentSubMode));
  238. }
  239. const AZ::Transform localFromWorld = m_worldFromLocal.GetInverse();
  240. const AZ::Vector3 localRayOrigin =
  241. localFromWorld.TransformPoint(mouseInteraction.m_mouseInteraction.m_mousePick.m_rayOrigin);
  242. const AZ::Vector3 localRayDirection = AzToolsFramework::TransformDirectionNoScaling(
  243. localFromWorld, mouseInteraction.m_mouseInteraction.m_mousePick.m_rayDirection);
  244. const int viewportId = mouseInteraction.m_mouseInteraction.m_interactionId.m_viewportId;
  245. const AzFramework::CameraState cameraState = AzToolsFramework::GetCameraState(viewportId);
  246. const AZStd::optional<EdgeIntersection> edgeIntersection = FindClosestEdgeIntersection(
  247. m_intersectionAndRenderData->m_whiteBoxIntersectionData, localRayOrigin, localRayDirection,
  248. m_worldFromLocal, cameraState);
  249. const AZStd::optional<PolygonIntersection> polygonIntersection = FindClosestPolygonIntersection(
  250. m_intersectionAndRenderData->m_whiteBoxIntersectionData, localRayOrigin, localRayDirection);
  251. const AZStd::optional<VertexIntersection> vertexIntersection = FindClosestVertexIntersection(
  252. m_intersectionAndRenderData->m_whiteBoxIntersectionData, localRayOrigin, localRayDirection,
  253. m_worldFromLocal, cameraState);
  254. // interactionHandled will be set to true if the mouse interaction has been handled by this white box component
  255. // which involves either interacting with a manipulator from this white box or clicking on the white box mesh
  256. // itself
  257. bool interactionHandled = AZStd::visit(
  258. [&mouseInteraction, entityComponentIdPair = GetEntityComponentIdPair(), &edgeIntersection,
  259. &polygonIntersection, &vertexIntersection](auto& mode)
  260. {
  261. return mode->HandleMouseInteraction(
  262. mouseInteraction, entityComponentIdPair, edgeIntersection, polygonIntersection, vertexIntersection);
  263. },
  264. m_modes);
  265. if (mouseInteraction.m_mouseInteraction.m_mouseButtons.Left() &&
  266. mouseInteraction.m_mouseEvent == AzToolsFramework::ViewportInteraction::MouseEvent::Up &&
  267. (edgeIntersection || polygonIntersection || vertexIntersection))
  268. {
  269. interactionHandled = true;
  270. }
  271. return interactionHandled;
  272. }
  273. AZStd::string EditorWhiteBoxComponentMode::GetComponentModeName() const
  274. {
  275. return "White Box Edit Mode";
  276. }
  277. AZ::Uuid EditorWhiteBoxComponentMode::GetComponentModeType() const
  278. {
  279. return azrtti_typeid<EditorWhiteBoxComponentMode>();
  280. }
  281. AZStd::vector<AzToolsFramework::ActionOverride> EditorWhiteBoxComponentMode::PopulateActionsImpl()
  282. {
  283. return AZStd::visit(
  284. [entityComponentIdPair = GetEntityComponentIdPair()](auto& mode)
  285. {
  286. return mode->PopulateActions(entityComponentIdPair);
  287. },
  288. m_modes);
  289. }
  290. void EditorWhiteBoxComponentMode::EnterDefaultMode()
  291. {
  292. m_modes = AZStd::make_unique<DefaultMode>(GetEntityComponentIdPair());
  293. m_intersectionAndRenderData = {};
  294. m_currentSubMode = SubMode::Default;
  295. SetViewportUiClusterActiveButton(m_modeSelectionClusterId, m_defaultModeButtonId);
  296. // Change sub-mode to default at the next frame to go after the automated mode switching in ComponentModeActionHandler.
  297. QTimer::singleShot(
  298. 0,
  299. []()
  300. {
  301. // Set the Action Context Mode in the Action Manager, if enabled.
  302. auto actionManagerInterface = AZ::Interface<AzToolsFramework::ActionManagerInterface>::Get();
  303. if (actionManagerInterface)
  304. {
  305. actionManagerInterface->SetActiveActionContextMode(
  306. EditorIdentifiers::MainWindowActionContextIdentifier, WhiteBoxDefaultSubModeIdentifier);
  307. }
  308. }
  309. );
  310. }
  311. void EditorWhiteBoxComponentMode::EnterEdgeRestoreMode()
  312. {
  313. m_modes = AZStd::make_unique<EdgeRestoreMode>();
  314. m_intersectionAndRenderData = {};
  315. m_currentSubMode = SubMode::EdgeRestore;
  316. SetViewportUiClusterActiveButton(m_modeSelectionClusterId, m_edgeRestoreModeButtonId);
  317. // Set the Action Context Mode in the Action Manager, if enabled.
  318. auto actionManagerInterface = AZ::Interface<AzToolsFramework::ActionManagerInterface>::Get();
  319. if (actionManagerInterface)
  320. {
  321. actionManagerInterface->SetActiveActionContextMode(EditorIdentifiers::MainWindowActionContextIdentifier, WhiteBoxEdgeRestoreSubModeIdentifier);
  322. }
  323. }
  324. void EditorWhiteBoxComponentMode::EnterTransformMode()
  325. {
  326. m_modes = AZStd::make_unique<TransformMode>(GetEntityComponentIdPair());
  327. m_intersectionAndRenderData = {};
  328. m_currentSubMode = SubMode::Transform;
  329. SetViewportUiClusterActiveButton(m_modeSelectionClusterId, m_transformModeButtonId);
  330. // Set the Action Context Mode in the Action Manager, if enabled.
  331. auto actionManagerInterface = AZ::Interface<AzToolsFramework::ActionManagerInterface>::Get();
  332. if (actionManagerInterface)
  333. {
  334. actionManagerInterface->SetActiveActionContextMode(EditorIdentifiers::MainWindowActionContextIdentifier, WhiteBoxTransformSubModeIdentifier);
  335. }
  336. }
  337. void EditorWhiteBoxComponentMode::DisplayEntityViewport(
  338. [[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay)
  339. {
  340. AZ_PROFILE_FUNCTION(AzToolsFramework);
  341. const auto modifiers = m_keyboardModifierQueryFn();
  342. // handle mode switch
  343. {
  344. auto* defaultMode = AZStd::get_if<AZStd::unique_ptr<DefaultMode>>(&m_modes);
  345. auto* edgeRestoreMode = AZStd::get_if<AZStd::unique_ptr<EdgeRestoreMode>>(&m_modes);
  346. // enter edge restore mode if inside normal mode and restore modifier is held
  347. if (RestoreModifier(modifiers))
  348. {
  349. if (defaultMode != nullptr)
  350. {
  351. EnterEdgeRestoreMode();
  352. }
  353. m_restoreModifierHeld = true;
  354. }
  355. // enter default mode if restore modifier is not currently held and
  356. // was held to enter restore mode (as opposed to viewport ui widget)
  357. else if (!RestoreModifier(modifiers))
  358. {
  359. if (m_restoreModifierHeld && edgeRestoreMode != nullptr)
  360. {
  361. EnterDefaultMode();
  362. }
  363. m_restoreModifierHeld = false;
  364. }
  365. }
  366. // generate mesh to query
  367. if (!m_intersectionAndRenderData.has_value())
  368. {
  369. RecalculateWhiteBoxIntersectionData(DecideEdgeSelectionMode(m_currentSubMode));
  370. }
  371. debugDisplay.DepthTestOn();
  372. debugDisplay.SetColor(ed_whiteBoxEdgeDefault);
  373. debugDisplay.SetLineWidth(4.0f);
  374. AZStd::visit(
  375. [entityComponentIdPair = GetEntityComponentIdPair(),
  376. &whiteBoxIntersectionAndRenderData = m_intersectionAndRenderData, viewportInfo, &debugDisplay,
  377. &worldFromLocal = m_worldFromLocal](auto& mode)
  378. {
  379. mode->Display(
  380. entityComponentIdPair, worldFromLocal, whiteBoxIntersectionAndRenderData.value(), viewportInfo,
  381. debugDisplay);
  382. },
  383. m_modes);
  384. debugDisplay.DepthTestOff();
  385. }
  386. void EditorWhiteBoxComponentMode::MarkWhiteBoxIntersectionDataDirty()
  387. {
  388. m_intersectionAndRenderData = {};
  389. }
  390. // combine user and mesh edge handles into a single collection
  391. static Api::EdgeHandles BuildAllEdgeHandles(const Api::EdgeTypes& edgeHandlesPair)
  392. {
  393. Api::EdgeHandles allEdgeHandles;
  394. allEdgeHandles.reserve(edgeHandlesPair.m_mesh.size() + edgeHandlesPair.m_user.size());
  395. allEdgeHandles.insert(allEdgeHandles.end(), edgeHandlesPair.m_mesh.cbegin(), edgeHandlesPair.m_mesh.cend());
  396. allEdgeHandles.insert(allEdgeHandles.end(), edgeHandlesPair.m_user.cbegin(), edgeHandlesPair.m_user.cend());
  397. return allEdgeHandles;
  398. }
  399. void EditorWhiteBoxComponentMode::RecalculateWhiteBoxIntersectionData(const EdgeSelectionType edgeSelectionMode)
  400. {
  401. AZ_PROFILE_FUNCTION(AzToolsFramework);
  402. WhiteBoxMesh* whiteBox = nullptr;
  403. EditorWhiteBoxComponentRequestBus::EventResult(
  404. whiteBox, GetEntityComponentIdPair(), &EditorWhiteBoxComponentRequests::GetWhiteBoxMesh);
  405. m_intersectionAndRenderData = IntersectionAndRenderData{};
  406. for (const auto& vertexHandle : Api::MeshVertexHandles(*whiteBox))
  407. {
  408. const auto vertexPosition = Api::VertexPosition(*whiteBox, vertexHandle);
  409. m_intersectionAndRenderData->m_whiteBoxIntersectionData.m_vertexBounds.emplace_back(
  410. VertexBoundWithHandle{{vertexPosition, cl_whiteBoxVertexManipulatorSize}, vertexHandle});
  411. }
  412. for (const auto& polygonHandle : Api::MeshPolygonHandles(*whiteBox))
  413. {
  414. const auto triangles = Api::FacesPositions(*whiteBox, polygonHandle.m_faceHandles);
  415. m_intersectionAndRenderData->m_whiteBoxIntersectionData.m_polygonBounds.emplace_back(
  416. PolygonBoundWithHandle{{triangles}, polygonHandle});
  417. }
  418. const auto edgeHandlesPair = Api::MeshUserEdgeHandles(*whiteBox);
  419. const auto edgeHandles = [edgeSelectionMode, &edgeHandlesPair]()
  420. {
  421. switch (edgeSelectionMode)
  422. {
  423. case EdgeSelectionType::Polygon:
  424. return edgeHandlesPair.m_user;
  425. case EdgeSelectionType::All:
  426. return BuildAllEdgeHandles(edgeHandlesPair);
  427. default:
  428. return Api::EdgeHandles{};
  429. }
  430. }();
  431. // all edges that are valid to interact with at this time
  432. for (const auto& edgeHandle : edgeHandles)
  433. {
  434. const auto edge = Api::EdgeVertexPositions(*whiteBox, edgeHandle);
  435. m_intersectionAndRenderData->m_whiteBoxIntersectionData.m_edgeBounds.emplace_back(
  436. EdgeBoundWithHandle{EdgeBound{edge[0], edge[1], cl_whiteBoxEdgeSelectionWidth}, edgeHandle});
  437. }
  438. // handle drawing 'user' and 'mesh' edges slightly differently
  439. for (const auto& edgeHandle : edgeHandlesPair.m_user)
  440. {
  441. const auto edge = Api::EdgeVertexPositions(*whiteBox, edgeHandle);
  442. m_intersectionAndRenderData->m_whiteBoxEdgeRenderData.m_bounds.m_user.emplace_back(
  443. EdgeBoundWithHandle{EdgeBound{edge[0], edge[1], cl_whiteBoxEdgeSelectionWidth}, edgeHandle});
  444. }
  445. for (const auto& edgeHandle : edgeHandlesPair.m_mesh)
  446. {
  447. const auto edge = Api::EdgeVertexPositions(*whiteBox, edgeHandle);
  448. m_intersectionAndRenderData->m_whiteBoxEdgeRenderData.m_bounds.m_mesh.emplace_back(
  449. EdgeBoundWithHandle{EdgeBound{edge[0], edge[1], cl_whiteBoxEdgeSelectionWidth}, edgeHandle});
  450. }
  451. }
  452. void EditorWhiteBoxComponentMode::OnTransformChanged(
  453. [[maybe_unused]] const AZ::Transform& local, const AZ::Transform& world)
  454. {
  455. m_worldFromLocal = world;
  456. }
  457. void EditorWhiteBoxComponentMode::OnDefaultShapeTypeChanged([[maybe_unused]] const DefaultShapeType defaultShape)
  458. {
  459. // ensure the mode and all modifiers are refreshed
  460. Refresh();
  461. }
  462. void EditorWhiteBoxComponentMode::OverrideKeyboardModifierQuery(
  463. const KeyboardModifierQueryFn& keyboardModifierQueryFn)
  464. {
  465. m_keyboardModifierQueryFn = keyboardModifierQueryFn;
  466. }
  467. static AzToolsFramework::ViewportUi::ButtonId RegisterClusterButton(
  468. AzToolsFramework::ViewportUi::ClusterId clusterId, const char* iconName)
  469. {
  470. AzToolsFramework::ViewportUi::ButtonId buttonId;
  471. AzToolsFramework::ViewportUi::ViewportUiRequestBus::EventResult(
  472. buttonId, AzToolsFramework::ViewportUi::DefaultViewportId,
  473. &AzToolsFramework::ViewportUi::ViewportUiRequestBus::Events::CreateClusterButton, clusterId,
  474. AZStd::string::format(":/stylesheet/img/UI20/toolbar/%s.svg", iconName));
  475. return buttonId;
  476. }
  477. void EditorWhiteBoxComponentMode::RemoveSubModeSelectionCluster()
  478. {
  479. AzToolsFramework::ViewportUi::ViewportUiRequestBus::Event(
  480. AzToolsFramework::ViewportUi::DefaultViewportId, &AzToolsFramework::ViewportUi::ViewportUiRequestBus::Events::RemoveCluster,
  481. m_modeSelectionClusterId);
  482. }
  483. void EditorWhiteBoxComponentMode::CreateSubModeSelectionCluster()
  484. {
  485. // create the cluster for changing transform mode
  486. AzToolsFramework::ViewportUi::ViewportUiRequestBus::EventResult(
  487. m_modeSelectionClusterId, AzToolsFramework::ViewportUi::DefaultViewportId,
  488. &AzToolsFramework::ViewportUi::ViewportUiRequestBus::Events::CreateCluster, AzToolsFramework::ViewportUi::Alignment::TopLeft);
  489. // create and register the buttons
  490. m_defaultModeButtonId = RegisterClusterButton(m_modeSelectionClusterId, "SketchMode");
  491. m_edgeRestoreModeButtonId = RegisterClusterButton(m_modeSelectionClusterId, "RestoreMode");
  492. // temporary setting to disable this feature
  493. if (AZ::SettingsRegistryInterface* settingsRegistry = AZ::SettingsRegistry::Get())
  494. {
  495. bool hasTransformMode = false;
  496. settingsRegistry->Get(hasTransformMode, WhiteBoxTransformFeature);
  497. if (hasTransformMode)
  498. {
  499. //TODO: this is a temporary icon
  500. m_transformModeButtonId = RegisterClusterButton(m_modeSelectionClusterId, "Align_to_Object");
  501. AzToolsFramework::ViewportUi::ViewportUiRequestBus::Event(
  502. AzToolsFramework::ViewportUi::DefaultViewportId,
  503. &AzToolsFramework::ViewportUi::ViewportUiRequestBus::Events::SetClusterButtonTooltip, m_modeSelectionClusterId,
  504. m_transformModeButtonId, WhiteboxModeClusterManipulatorTooltip);
  505. }
  506. }
  507. // set button tooltips
  508. AzToolsFramework::ViewportUi::ViewportUiRequestBus::Event(
  509. AzToolsFramework::ViewportUi::DefaultViewportId,
  510. &AzToolsFramework::ViewportUi::ViewportUiRequestBus::Events::SetClusterButtonTooltip, m_modeSelectionClusterId,
  511. m_defaultModeButtonId, WhiteboxModeClusterDefaultTooltip);
  512. AzToolsFramework::ViewportUi::ViewportUiRequestBus::Event(
  513. AzToolsFramework::ViewportUi::DefaultViewportId,
  514. &AzToolsFramework::ViewportUi::ViewportUiRequestBus::Events::SetClusterButtonTooltip, m_modeSelectionClusterId,
  515. m_edgeRestoreModeButtonId, WhiteboxModeClusterEdgeRestoreTooltip);
  516. m_modeSelectionHandler = AZ::Event<AzToolsFramework::ViewportUi::ButtonId>::Handler(
  517. [this](AzToolsFramework::ViewportUi::ButtonId buttonId)
  518. {
  519. if (buttonId == m_defaultModeButtonId)
  520. {
  521. EnterDefaultMode();
  522. }
  523. else if (buttonId == m_edgeRestoreModeButtonId)
  524. {
  525. EnterEdgeRestoreMode();
  526. }
  527. else if (buttonId == m_transformModeButtonId)
  528. {
  529. EnterTransformMode();
  530. }
  531. });
  532. AzToolsFramework::ViewportUi::ViewportUiRequestBus::Event(
  533. AzToolsFramework::ViewportUi::DefaultViewportId,
  534. &AzToolsFramework::ViewportUi::ViewportUiRequestBus::Events::RegisterClusterEventHandler, m_modeSelectionClusterId,
  535. m_modeSelectionHandler);
  536. }
  537. } // namespace WhiteBox