WhiteBoxVertexTranslationModifier.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  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 "SubComponentModes/EditorWhiteBoxDefaultModeBus.h"
  9. #include "Util/WhiteBoxMathUtil.h"
  10. #include "Viewport/WhiteBoxModifierUtil.h"
  11. #include "WhiteBox/WhiteBoxToolApi.h"
  12. #include "WhiteBoxVertexTranslationModifier.h"
  13. #include <AzCore/Debug/Trace.h>
  14. #include <AzFramework/Viewport/ViewportScreen.h>
  15. #include <AzToolsFramework/Manipulators/ManipulatorManager.h>
  16. #include <AzToolsFramework/Manipulators/ManipulatorView.h>
  17. #include <AzToolsFramework/Manipulators/MultiLinearManipulator.h>
  18. #include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
  19. #include <EditorWhiteBoxComponentModeBus.h>
  20. #include <WhiteBox/EditorWhiteBoxComponentBus.h>
  21. AZ_CVAR(
  22. float, cl_whiteBoxVertexTranslationPressTime, 0.1f, nullptr, AZ::ConsoleFunctorFlags::Null,
  23. "How long must the modifier be held before we display the axes the vertex can be moved along");
  24. AZ_CVAR(
  25. float, cl_whiteBoxVertexTranslationAxisLength, 500.0f, nullptr, AZ::ConsoleFunctorFlags::Null,
  26. "The length of the vertex translation axis to draw while moving the vertex");
  27. AZ_CVAR(
  28. AZ::Color, cl_whiteBoxVertexTranslationAxisColor, AZ::Color::CreateFromRgba(255, 100, 0, 255), nullptr,
  29. AZ::ConsoleFunctorFlags::Null, "The color of the vertex translation axes before movement has occurred");
  30. AZ_CVAR(
  31. AZ::Color, cl_whiteBoxVertexTranslationAxisInactiveColor, AZ::Color::CreateFromRgba(255, 100, 0, 90), nullptr,
  32. AZ::ConsoleFunctorFlags::Null, "The color of the vertex translation axes after movement has occurred");
  33. AZ_CVAR(
  34. AZ::Color, cl_whiteBoxVertexSelectedTranslationAxisColor, AZ::Color::CreateFromRgba(0, 150, 255, 255), nullptr,
  35. AZ::ConsoleFunctorFlags::Null, "The color of the vertex translation axis the vertex is moving along");
  36. AZ_CVAR(
  37. float, cl_whiteBoxVertexTranslationAxisWidth, 5.0f, nullptr, AZ::ConsoleFunctorFlags::Null,
  38. "The thickness of the line for the vertex translation axes");
  39. namespace WhiteBox
  40. {
  41. AZ_CLASS_ALLOCATOR_IMPL(VertexTranslationModifier, AZ::SystemAllocator)
  42. static bool IsAxisValid(const int axisIndex)
  43. {
  44. return axisIndex != VertexTranslationModifier::InvalidAxisIndex;
  45. }
  46. VertexTranslationModifier::VertexTranslationModifier(
  47. const AZ::EntityComponentIdPair& entityComponentIdPair, Api::VertexHandle vertexHandle,
  48. [[maybe_unused]] const AZ::Vector3& intersectionPoint)
  49. : m_entityComponentIdPair{entityComponentIdPair}
  50. , m_vertexHandle{vertexHandle}
  51. {
  52. CreateManipulator();
  53. AzFramework::ViewportDebugDisplayEventBus::Handler::BusConnect(AzToolsFramework::GetEntityContextId());
  54. }
  55. VertexTranslationModifier::~VertexTranslationModifier()
  56. {
  57. AzFramework::ViewportDebugDisplayEventBus::Handler::BusDisconnect();
  58. DestroyManipulator();
  59. }
  60. static int FindClosestAxis(
  61. const AZ::EntityId entityId, const AzToolsFramework::MultiLinearManipulator::Action& action,
  62. const AZStd::vector<AZStd::pair<AZ::Vector3, AZ::Vector3>>& edgeBeginEnds)
  63. {
  64. const auto cameraState = AzToolsFramework::GetCameraState(action.m_viewportId);
  65. const auto worldFromLocal = AzToolsFramework::WorldFromLocalWithUniformScale(entityId);
  66. int axisIndex = VertexTranslationModifier::InvalidAxisIndex;
  67. float maxLength = 0.0f;
  68. for (size_t actionIndex = 0; actionIndex < action.m_actions.size(); ++actionIndex)
  69. {
  70. const auto& currentAction = action.m_actions[actionIndex];
  71. const auto& edgeAxis = edgeBeginEnds[actionIndex];
  72. const auto worldStart = worldFromLocal.TransformPoint(edgeAxis.first);
  73. const auto worldEnd = worldFromLocal.TransformPoint(edgeAxis.second);
  74. const auto screenAxis = AzFramework::Vector2FromScreenVector(
  75. AzFramework::WorldToScreen(worldEnd, cameraState) -
  76. AzFramework::WorldToScreen(worldStart, cameraState))
  77. .GetNormalizedSafe();
  78. const auto screenLength = std::fabs(currentAction.ScreenOffset().Dot(screenAxis));
  79. if (screenLength > maxLength)
  80. {
  81. axisIndex = static_cast<int>(actionIndex);
  82. maxLength = screenLength;
  83. }
  84. }
  85. return axisIndex;
  86. }
  87. void VertexTranslationModifier::CreateManipulator()
  88. {
  89. using AzToolsFramework::MultiLinearManipulator;
  90. using AzToolsFramework::ViewportInteraction::MouseInteraction;
  91. WhiteBoxMesh* whiteBox = nullptr;
  92. EditorWhiteBoxComponentRequestBus::EventResult(
  93. whiteBox, m_entityComponentIdPair, &EditorWhiteBoxComponentRequests::GetWhiteBoxMesh);
  94. // create the manipulator in the local space of the entity the white box component is on
  95. m_translationManipulator = MultiLinearManipulator::MakeShared(
  96. AzToolsFramework::WorldFromLocalWithUniformScale(m_entityComponentIdPair.GetEntityId()));
  97. m_translationManipulator->Register(AzToolsFramework::g_mainManipulatorManagerId);
  98. m_translationManipulator->AddEntityComponentIdPair(m_entityComponentIdPair);
  99. m_translationManipulator->SetLocalPosition(Api::VertexPosition(*whiteBox, m_vertexHandle));
  100. // add all axes connecting to vertex
  101. m_translationManipulator->AddAxes(Api::VertexUserEdgeAxes(*whiteBox, m_vertexHandle));
  102. struct SharedState
  103. {
  104. // the previous position when moving the manipulator, used to calculate manipulator delta position
  105. AZ::Vector3 m_prevPosition;
  106. // what state of appending are we currently in
  107. AppendStage m_appendStage = AppendStage::None;
  108. // store all begin and end positions for each edge
  109. AZStd::vector<AZStd::pair<AZ::Vector3, AZ::Vector3>> m_edgeBeginEnds;
  110. // has the modifier moved during the action
  111. bool m_moved = false;
  112. // copy of the whitebox mesh before modifying
  113. Api::WhiteBoxMeshPtr m_originalMesh = nullptr;
  114. };
  115. auto sharedState = AZStd::make_shared<SharedState>();
  116. CreateView();
  117. // setup callback for translation (linear) manipulator
  118. m_translationManipulator->InstallLeftMouseDownCallback(
  119. [this, sharedState]([[maybe_unused]] const MultiLinearManipulator::Action& action)
  120. {
  121. WhiteBoxMesh* whiteBox = nullptr;
  122. EditorWhiteBoxComponentRequestBus::EventResult(
  123. whiteBox, m_entityComponentIdPair, &EditorWhiteBoxComponentRequests::GetWhiteBoxMesh);
  124. sharedState->m_appendStage = AppendStage::None;
  125. sharedState->m_moved = false;
  126. sharedState->m_originalMesh = Api::CloneMesh(*whiteBox);
  127. sharedState->m_edgeBeginEnds.clear();
  128. m_actionIndex = InvalidAxisIndex;
  129. m_localPositionAtMouseDown = m_translationManipulator->GetLocalPosition();
  130. for (const auto& edgeHandle : Api::VertexUserEdgeHandles(*whiteBox, m_vertexHandle))
  131. {
  132. const auto edgeVertexPositions = Api::EdgeVertexPositions(*whiteBox, edgeHandle);
  133. sharedState->m_edgeBeginEnds.push_back(
  134. AZStd::make_pair(edgeVertexPositions[0], edgeVertexPositions[1]));
  135. }
  136. this->AZ::TickBus::Handler::BusConnect();
  137. });
  138. m_translationManipulator->InstallMouseMoveCallback(
  139. [this, sharedState](const MultiLinearManipulator::Action& action)
  140. {
  141. WhiteBoxMesh* whiteBox = nullptr;
  142. EditorWhiteBoxComponentRequestBus::EventResult(
  143. whiteBox, m_entityComponentIdPair, &EditorWhiteBoxComponentRequests::GetWhiteBoxMesh);
  144. m_actionIndex =
  145. FindClosestAxis(m_entityComponentIdPair.GetEntityId(), action, sharedState->m_edgeBeginEnds);
  146. if (m_actionIndex != InvalidAxisIndex)
  147. {
  148. // has the modifier moved during this interaction
  149. sharedState->m_moved = sharedState->m_moved ||
  150. action.m_actions[m_actionIndex].LocalPositionOffset().GetLength() >=
  151. cl_whiteBoxMouseClickDeltaThreshold;
  152. // update vertex and position of manipulator
  153. Api::SetVertexPosition(*whiteBox, m_vertexHandle, action.m_actions[m_actionIndex].LocalPosition());
  154. m_translationManipulator->SetLocalPosition(Api::VertexPosition(*whiteBox, m_vertexHandle));
  155. EditorWhiteBoxComponentModeRequestBus::Event(
  156. m_entityComponentIdPair,
  157. &EditorWhiteBoxComponentModeRequestBus::Events::MarkWhiteBoxIntersectionDataDirty);
  158. EditorWhiteBoxDefaultModeRequestBus::Event(
  159. m_entityComponentIdPair,
  160. &EditorWhiteBoxDefaultModeRequestBus::Events::RefreshPolygonTranslationModifier);
  161. EditorWhiteBoxDefaultModeRequestBus::Event(
  162. m_entityComponentIdPair,
  163. &EditorWhiteBoxDefaultModeRequestBus::Events::RefreshPolygonScaleModifier);
  164. EditorWhiteBoxDefaultModeRequestBus::Event(
  165. m_entityComponentIdPair,
  166. &EditorWhiteBoxDefaultModeRequestBus::Events::RefreshEdgeTranslationModifier);
  167. EditorWhiteBoxDefaultModeRequestBus::Event(
  168. m_entityComponentIdPair,
  169. &EditorWhiteBoxDefaultModeRequestBus::Events::RefreshEdgeScaleModifier);
  170. EditorWhiteBoxComponentNotificationBus::Event(
  171. m_entityComponentIdPair,
  172. &EditorWhiteBoxComponentNotificationBus::Events::OnWhiteBoxMeshModified);
  173. }
  174. Api::CalculateNormals(*whiteBox);
  175. Api::CalculatePlanarUVs(*whiteBox);
  176. });
  177. m_translationManipulator->InstallInvalidateCallback(
  178. [this, sharedState]()
  179. {
  180. WhiteBoxMesh* whiteBox = nullptr;
  181. EditorWhiteBoxComponentRequestBus::EventResult(
  182. whiteBox, m_entityComponentIdPair, &EditorWhiteBoxComponentRequests::GetWhiteBoxMesh);
  183. Api::WhiteBoxMeshStream clondData;
  184. if (sharedState->m_originalMesh && Api::ReadMesh(*sharedState->m_originalMesh.get(), clondData) == Api::ReadResult::Full)
  185. {
  186. if (!Api::WriteMesh(*whiteBox, clondData))
  187. {
  188. AZ_Error("WhiteBox", false, "failed to restore WhiteBox mesh");
  189. }
  190. }
  191. sharedState->m_originalMesh = nullptr;
  192. m_pressTime = 0.0f;
  193. m_actionIndex = InvalidAxisIndex;
  194. this->AZ::TickBus::Handler::BusDisconnect();
  195. });
  196. m_translationManipulator->InstallLeftMouseUpCallback(
  197. [this, sharedState,
  198. translationManipulator = AZStd::weak_ptr<MultiLinearManipulator>(m_translationManipulator)](
  199. [[maybe_unused]] const MultiLinearManipulator::Action& action)
  200. {
  201. // we haven't moved, count as a click
  202. if (!sharedState->m_moved)
  203. {
  204. EditorWhiteBoxDefaultModeRequestBus::Event(
  205. m_entityComponentIdPair,
  206. &EditorWhiteBoxDefaultModeRequestBus::Events::AssignSelectedVertexSelectionModifier);
  207. }
  208. else
  209. {
  210. WhiteBoxMesh* whiteBox = nullptr;
  211. EditorWhiteBoxComponentRequestBus::EventResult(
  212. whiteBox, m_entityComponentIdPair, &EditorWhiteBoxComponentRequests::GetWhiteBoxMesh);
  213. // refresh and update all manipulator axes after mouse up
  214. if (auto manipulator = translationManipulator.lock())
  215. {
  216. manipulator->ClearAxes();
  217. manipulator->AddAxes(Api::VertexUserEdgeAxes(*whiteBox, m_vertexHandle));
  218. }
  219. sharedState->m_originalMesh = nullptr;
  220. EditorWhiteBoxComponentRequestBus::Event(
  221. m_entityComponentIdPair, &EditorWhiteBoxComponentRequests::SerializeWhiteBox);
  222. }
  223. m_pressTime = 0.0f;
  224. m_actionIndex = InvalidAxisIndex;
  225. this->AZ::TickBus::Handler::BusDisconnect();
  226. });
  227. }
  228. void VertexTranslationModifier::CreateView()
  229. {
  230. using AzToolsFramework::ViewportInteraction::MouseInteraction;
  231. if (!m_vertexView)
  232. {
  233. m_vertexView = AzToolsFramework::CreateManipulatorViewSphere(
  234. m_color, cl_whiteBoxVertexManipulatorSize,
  235. []([[maybe_unused]] const MouseInteraction& mouseInteraction, [[maybe_unused]] const bool mouseOver,
  236. const AZ::Color& defaultColor)
  237. {
  238. return defaultColor;
  239. });
  240. }
  241. m_vertexView->m_color = m_color;
  242. m_translationManipulator->SetViews(AzToolsFramework::ManipulatorViews{m_vertexView});
  243. }
  244. void VertexTranslationModifier::DestroyManipulator()
  245. {
  246. m_translationManipulator->Unregister();
  247. m_translationManipulator.reset();
  248. }
  249. bool VertexTranslationModifier::MouseOver() const
  250. {
  251. return m_translationManipulator->MouseOver();
  252. }
  253. void VertexTranslationModifier::ForwardMouseOverEvent(
  254. const AzToolsFramework::ViewportInteraction::MouseInteraction& interaction)
  255. {
  256. m_translationManipulator->ForwardMouseOverEvent(interaction);
  257. }
  258. void VertexTranslationModifier::Refresh()
  259. {
  260. DestroyManipulator();
  261. CreateManipulator();
  262. }
  263. bool VertexTranslationModifier::PerformingAction() const
  264. {
  265. return m_translationManipulator->PerformingAction();
  266. }
  267. void VertexTranslationModifier::DisplayViewport(
  268. [[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay)
  269. {
  270. if (PerformingAction() && m_pressTime >= cl_whiteBoxVertexTranslationPressTime)
  271. {
  272. const auto worldFromLocal =
  273. AzToolsFramework::WorldFromLocalWithUniformScale(m_entityComponentIdPair.GetEntityId());
  274. debugDisplay.PushMatrix(worldFromLocal);
  275. debugDisplay.DepthTestOff();
  276. debugDisplay.SetLineWidth(cl_whiteBoxVertexTranslationAxisWidth);
  277. AZStd::for_each(
  278. m_translationManipulator->FixedBegin(), m_translationManipulator->FixedEnd(),
  279. [this, &debugDisplay, actionIndex = 0](const AzToolsFramework::LinearManipulator::Fixed& fixed) mutable
  280. {
  281. if (!IsAxisValid(m_actionIndex))
  282. {
  283. debugDisplay.SetColor(cl_whiteBoxVertexTranslationAxisColor);
  284. }
  285. else
  286. {
  287. debugDisplay.SetColor(
  288. actionIndex == m_actionIndex ? cl_whiteBoxVertexSelectedTranslationAxisColor
  289. : cl_whiteBoxVertexTranslationAxisInactiveColor);
  290. }
  291. const float axisLength = cl_whiteBoxVertexTranslationAxisLength;
  292. debugDisplay.DrawLine(
  293. m_localPositionAtMouseDown - fixed.m_axis * axisLength * 0.5f,
  294. m_localPositionAtMouseDown + fixed.m_axis * axisLength * 0.5f);
  295. actionIndex++;
  296. });
  297. debugDisplay.DepthTestOn();
  298. debugDisplay.PopMatrix();
  299. }
  300. }
  301. void VertexTranslationModifier::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
  302. {
  303. m_pressTime += deltaTime;
  304. }
  305. } // namespace WhiteBox