ArcBallControllerComponent.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  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 <Atom/Component/DebugCamera/ArcBallControllerComponent.h>
  9. #include <AzCore/Component/TransformBus.h>
  10. #include <AzCore/RTTI/BehaviorContext.h>
  11. #include <AzCore/Serialization/SerializeContext.h>
  12. #include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
  13. #include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
  14. #include <AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.h>
  15. #include <AzFramework/Input/Devices/Touch/InputDeviceTouch.h>
  16. #include <AzFramework/Windowing/WindowBus.h>
  17. #include <DebugCameraUtils.h>
  18. namespace AZ
  19. {
  20. namespace Debug
  21. {
  22. ArcBallControllerComponent::ArcBallControllerComponent()
  23. : AzFramework::InputChannelEventListener(AzFramework::InputChannelEventListener::GetPriorityDefault())
  24. {}
  25. void ArcBallControllerComponent::Reflect(AZ::ReflectContext* reflection)
  26. {
  27. if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection))
  28. {
  29. serializeContext->Class<ArcBallControllerComponent, CameraControllerComponent, AZ::Component>()
  30. ->Version(1);
  31. }
  32. if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(reflection))
  33. {
  34. behaviorContext->EBus<ArcBallControllerRequestBus>("ArcBallControllerRequestBus")
  35. ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
  36. ->Attribute(AZ::Script::Attributes::Category, "Camera")
  37. ->Attribute(AZ::Script::Attributes::Module, "render")
  38. ->Event("SetCenter", &ArcBallControllerRequestBus::Events::SetCenter)
  39. ->Event("SetPan", &ArcBallControllerRequestBus::Events::SetPan)
  40. ->Event("SetDistance", &ArcBallControllerRequestBus::Events::SetDistance)
  41. ->Event("SetMinDistance", &ArcBallControllerRequestBus::Events::SetMinDistance)
  42. ->Event("SetMaxDistance", &ArcBallControllerRequestBus::Events::SetMaxDistance)
  43. ->Event("SetHeading", &ArcBallControllerRequestBus::Events::SetHeading)
  44. ->Event("SetPitch", &ArcBallControllerRequestBus::Events::SetPitch)
  45. ->Event("SetPanningSensitivity", &ArcBallControllerRequestBus::Events::SetPanningSensitivity)
  46. ->Event("SetZoomingSensitivity", &ArcBallControllerRequestBus::Events::SetZoomingSensitivity)
  47. ->Event("GetCenter", &ArcBallControllerRequestBus::Events::GetCenter)
  48. ->Event("GetPan", &ArcBallControllerRequestBus::Events::GetPan)
  49. ->Event("GetDistance", &ArcBallControllerRequestBus::Events::GetDistance)
  50. ->Event("GetMinDistance", &ArcBallControllerRequestBus::Events::GetMinDistance)
  51. ->Event("GetMaxDistance", &ArcBallControllerRequestBus::Events::GetMaxDistance)
  52. ->Event("GetHeading", &ArcBallControllerRequestBus::Events::GetHeading)
  53. ->Event("GetPitch", &ArcBallControllerRequestBus::Events::GetPitch)
  54. ->Event("GetPanningSensitivity", &ArcBallControllerRequestBus::Events::GetPanningSensitivity)
  55. ->Event("GetZoomingSensitivity", &ArcBallControllerRequestBus::Events::GetZoomingSensitivity)
  56. ;
  57. }
  58. }
  59. void ArcBallControllerComponent::OnEnabled()
  60. {
  61. // Reset parameters with initial values
  62. m_arcballActive = false;
  63. m_panningActive = false;
  64. m_center = AZ::Vector3::CreateZero();
  65. m_panningOffset = AZ::Vector3::CreateZero();
  66. m_panningOffsetDelta = AZ::Vector3::CreateZero();
  67. m_distance = 5.0f;
  68. m_minDistance = 0.1f;
  69. m_maxDistance = 10.0f;
  70. m_currentHeading = 0.0f;
  71. m_currentPitch = 0.0f;
  72. m_panningSensitivity = 1.0f;
  73. m_zoomingSensitivity = 1.0f;
  74. AzFramework::NativeWindowHandle windowHandle = nullptr;
  75. AzFramework::WindowSystemRequestBus::BroadcastResult(
  76. windowHandle,
  77. &AzFramework::WindowSystemRequestBus::Events::GetDefaultWindowHandle);
  78. AzFramework::WindowSize windowSize;
  79. AzFramework::WindowRequestBus::EventResult(
  80. windowSize,
  81. windowHandle,
  82. &AzFramework::WindowRequestBus::Events::GetRenderResolution);
  83. m_windowWidth = windowSize.m_width;
  84. m_windowHeight = windowSize.m_height;
  85. ArcBallControllerRequestBus::Handler::BusConnect(GetEntityId());
  86. AzFramework::InputChannelEventListener::Connect();
  87. AZ::TickBus::Handler::BusConnect();
  88. }
  89. void ArcBallControllerComponent::OnDisabled()
  90. {
  91. TickBus::Handler::BusDisconnect();
  92. AzFramework::InputChannelEventListener::Disconnect();
  93. ArcBallControllerRequestBus::Handler::BusDisconnect();
  94. }
  95. void ArcBallControllerComponent::OnTick(float deltaTime, AZ::ScriptTimePoint time)
  96. {
  97. AZ_UNUSED(time);
  98. if (m_distance < m_minDistance)
  99. {
  100. m_distance = m_minDistance;
  101. }
  102. else if (m_distance > m_maxDistance)
  103. {
  104. m_distance = m_maxDistance;
  105. }
  106. // The coordinate system is right-handed and Z-up. So heading is a rotation around the Z axis.
  107. // After that rotation we rotate around the (rotated by heading) X axis for pitch.
  108. AZ::Quaternion orientation = AZ::Quaternion::CreateRotationZ(m_currentHeading)
  109. * AZ::Quaternion::CreateRotationX(m_currentPitch);
  110. m_panningOffsetDelta *= deltaTime;
  111. m_panningOffset += (orientation.TransformVector(m_panningOffsetDelta));
  112. AZ::Vector3 position = (m_center + m_panningOffset) + (orientation.TransformVector(AZ::Vector3(0, -m_distance, 0)));
  113. AZ::Transform transform = AZ::Transform::CreateFromQuaternionAndTranslation(orientation, position);
  114. AZ::TransformBus::Event(
  115. GetEntityId(), &AZ::TransformBus::Events::SetLocalTM, transform);
  116. }
  117. bool ArcBallControllerComponent::OnInputChannelEventFiltered(const AzFramework::InputChannel& inputChannel)
  118. {
  119. static const float PixelToDegree = 1.0 / 360.0f;
  120. uint32_t handledChannels = ArcBallControllerChannel_None;
  121. const AzFramework::InputChannelId& inputChannelId = inputChannel.GetInputChannelId();
  122. switch (inputChannel.GetState())
  123. {
  124. case AzFramework::InputChannel::State::Began:
  125. case AzFramework::InputChannel::State::Updated: // update the camera rotation
  126. {
  127. //Keyboard & Mouse
  128. if (inputChannelId == AzFramework::InputDeviceMouse::Button::Right)
  129. {
  130. m_arcballActive = true;
  131. handledChannels |= ArcBallControllerChannel_Orientation;
  132. }
  133. else if (inputChannelId == AzFramework::InputDeviceMouse::Button::Middle)
  134. {
  135. m_panningActive = true;
  136. handledChannels |= ArcBallControllerChannel_Pan;
  137. }
  138. if (m_arcballActive)
  139. {
  140. if (inputChannelId == AzFramework::InputDeviceMouse::Movement::X)
  141. {
  142. // modify yaw angle
  143. m_currentHeading -= inputChannel.GetValue() * PixelToDegree;
  144. m_currentHeading = NormalizeAngle(m_currentHeading);
  145. }
  146. else if (inputChannelId == AzFramework::InputDeviceMouse::Movement::Y)
  147. {
  148. // modify pitch angle
  149. m_currentPitch -= inputChannel.GetValue() * PixelToDegree;
  150. m_currentPitch = AZStd::max(m_currentPitch, -AZ::Constants::HalfPi);
  151. m_currentPitch = AZStd::min(m_currentPitch, AZ::Constants::HalfPi);
  152. }
  153. }
  154. else if (m_panningActive)
  155. {
  156. if (inputChannelId == AzFramework::InputDeviceMouse::Movement::X)
  157. {
  158. m_panningOffsetDelta.SetX(-inputChannel.GetValue() * m_panningSensitivity);
  159. }
  160. else if (inputChannelId == AzFramework::InputDeviceMouse::Movement::Y)
  161. {
  162. m_panningOffsetDelta.SetZ(inputChannel.GetValue() * m_panningSensitivity);
  163. }
  164. }
  165. if (inputChannelId == AzFramework::InputDeviceMouse::Movement::Z)
  166. {
  167. const float MouseWheelDeltaScale = 1.0f / 120.0f; // based on WHEEL_DELTA in WinUser.h
  168. m_distance -= inputChannel.GetValue() * MouseWheelDeltaScale * m_zoomingSensitivity;
  169. m_zoomingActive = true;
  170. handledChannels |= ArcBallControllerChannel_Distance;
  171. }
  172. // Gamepad
  173. if (inputChannelId == AzFramework::InputDeviceGamepad::Trigger::L2)
  174. {
  175. m_arcballActive = true;
  176. handledChannels |= ArcBallControllerChannel_Orientation;
  177. }
  178. else if (inputChannelId == AzFramework::InputDeviceGamepad::Button::L1)
  179. {
  180. m_panningActive = true;
  181. handledChannels |= ArcBallControllerChannel_Pan;
  182. }
  183. if (m_arcballActive)
  184. {
  185. if (inputChannelId == AzFramework::InputDeviceGamepad::ThumbStickAxis1D::RX)
  186. {
  187. // modify yaw angle
  188. m_currentHeading -= inputChannel.GetValue() * PixelToDegree;
  189. m_currentHeading = NormalizeAngle(m_currentHeading);
  190. }
  191. else if (inputChannelId == AzFramework::InputDeviceGamepad::ThumbStickAxis1D::RY)
  192. {
  193. // modify pitch angle
  194. m_currentPitch += inputChannel.GetValue() * PixelToDegree;
  195. m_currentPitch = AZStd::max(m_currentPitch, -AZ::Constants::HalfPi);
  196. m_currentPitch = AZStd::min(m_currentPitch, AZ::Constants::HalfPi);
  197. }
  198. }
  199. else if (m_panningActive)
  200. {
  201. if (inputChannelId == AzFramework::InputDeviceGamepad::ThumbStickAxis1D::RX)
  202. {
  203. m_panningOffsetDelta.SetX(-inputChannel.GetValue() * 10.0f * m_panningSensitivity);
  204. }
  205. else if (inputChannelId == AzFramework::InputDeviceGamepad::ThumbStickAxis1D::RY)
  206. {
  207. m_panningOffsetDelta.SetZ(inputChannel.GetValue() * 10.0f * m_panningSensitivity);
  208. }
  209. }
  210. if (inputChannelId == AzFramework::InputDeviceGamepad::ThumbStickAxis1D::LY)
  211. {
  212. m_distance -= inputChannel.GetValue() * m_zoomingSensitivity;
  213. m_zoomingActive = true;
  214. handledChannels |= ArcBallControllerChannel_Distance;
  215. }
  216. // Touch controls works depending which side of the screen you start the touch event.
  217. // Left side controls the heading and pitch. Right side controls the panning.
  218. // Only one touch control can be active at the same time.
  219. if (inputChannelId == AzFramework::InputDeviceTouch::Touch::Index0)
  220. {
  221. auto* positionData = inputChannel.GetCustomData<AzFramework::InputChannel::PositionData2D>();
  222. AZ::Vector2 screenPos = positionData->m_normalizedPosition;
  223. auto deltaInPixels = screenPos - m_lastTouchPosition;
  224. deltaInPixels *= AZ::Vector2(static_cast<float>(m_windowWidth), static_cast<float>(m_windowHeight));
  225. if (inputChannel.GetState() == AzFramework::InputChannel::State::Began)
  226. {
  227. m_panningActive = screenPos.GetX() > 0.5f ? true : false;
  228. m_arcballActive = !m_panningActive;
  229. }
  230. else if(m_panningActive)
  231. {
  232. m_panningOffsetDelta.SetX(-deltaInPixels.GetX() * m_panningSensitivity);
  233. m_panningOffsetDelta.SetZ(deltaInPixels.GetY() * m_panningSensitivity);
  234. }
  235. else if(m_arcballActive)
  236. {
  237. // modify yaw angle
  238. m_currentHeading -= deltaInPixels.GetX() * PixelToDegree;
  239. m_currentHeading = NormalizeAngle(m_currentHeading);
  240. // modify pitch angle
  241. m_currentPitch -= deltaInPixels.GetY() * PixelToDegree;
  242. m_currentPitch = AZStd::max(m_currentPitch, -AZ::Constants::HalfPi);
  243. m_currentPitch = AZStd::min(m_currentPitch, AZ::Constants::HalfPi);
  244. }
  245. m_lastTouchPosition = screenPos;
  246. if (m_panningActive)
  247. {
  248. handledChannels |= ArcBallControllerChannel_Pan;
  249. }
  250. else if (m_arcballActive)
  251. {
  252. handledChannels |= ArcBallControllerChannel_Orientation;
  253. }
  254. }
  255. if (handledChannels && AzFramework::InputChannel::State::Began == inputChannel.GetState())
  256. {
  257. CameraControllerNotificationBus::Broadcast(&CameraControllerNotifications::OnCameraMoveBegan, RTTI_GetType(), handledChannels);
  258. }
  259. break;
  260. }
  261. case AzFramework::InputChannel::State::Ended: // update the released input state
  262. {
  263. uint32_t handledChannels2 = ArcBallControllerChannel_None;
  264. if (inputChannelId == AzFramework::InputDeviceMouse::Button::Right)
  265. {
  266. m_arcballActive = false;
  267. handledChannels2 |= ArcBallControllerChannel_Orientation;
  268. }
  269. else if (inputChannelId == AzFramework::InputDeviceMouse::Button::Middle)
  270. {
  271. m_panningActive = false;
  272. handledChannels2 |= ArcBallControllerChannel_Pan;
  273. }
  274. else if (inputChannelId == AzFramework::InputDeviceGamepad::Trigger::L2)
  275. {
  276. m_arcballActive = false;
  277. handledChannels2 |= ArcBallControllerChannel_Orientation;
  278. }
  279. else if (inputChannelId == AzFramework::InputDeviceGamepad::Button::L1)
  280. {
  281. m_panningActive = false;
  282. handledChannels2 |= ArcBallControllerChannel_Pan;
  283. }
  284. else if (inputChannelId == AzFramework::InputDeviceTouch::Touch::Index0)
  285. {
  286. if (m_panningActive)
  287. {
  288. handledChannels2 |= ArcBallControllerChannel_Pan;
  289. }
  290. else if (m_arcballActive)
  291. {
  292. handledChannels2 |= ArcBallControllerChannel_Orientation;
  293. }
  294. m_panningActive = false;
  295. m_arcballActive = false;
  296. }
  297. else if (inputChannelId == AzFramework::InputDeviceMouse::Movement::Z ||
  298. inputChannelId == AzFramework::InputDeviceGamepad::ThumbStickAxis1D::LY)
  299. {
  300. m_zoomingActive = false;
  301. handledChannels2 |= ArcBallControllerChannel_Distance;
  302. }
  303. if (handledChannels2)
  304. {
  305. CameraControllerNotificationBus::Broadcast(&CameraControllerNotifications::OnCameraMoveEnded, RTTI_GetType(), handledChannels2);
  306. }
  307. }
  308. default:
  309. {
  310. break;
  311. }
  312. }
  313. return false;
  314. }
  315. void ArcBallControllerComponent::SetCenter(AZ::Vector3 center)
  316. {
  317. m_center = center;
  318. }
  319. void ArcBallControllerComponent::SetPan(AZ::Vector3 pan)
  320. {
  321. m_panningOffset = pan;
  322. }
  323. void ArcBallControllerComponent::SetDistance(float distance)
  324. {
  325. m_distance = distance;
  326. }
  327. void ArcBallControllerComponent::SetMinDistance(float minDistance)
  328. {
  329. m_minDistance = minDistance;
  330. m_distance = AZ::GetMax(m_distance, m_minDistance);
  331. }
  332. void ArcBallControllerComponent::SetMaxDistance(float maxDistance)
  333. {
  334. m_maxDistance = maxDistance;
  335. m_distance = AZ::GetMin(m_distance, m_maxDistance);
  336. }
  337. void ArcBallControllerComponent::SetHeading(float heading)
  338. {
  339. m_currentHeading = heading;
  340. }
  341. void ArcBallControllerComponent::SetPitch(float pitch)
  342. {
  343. m_currentPitch = pitch;
  344. }
  345. void ArcBallControllerComponent::SetPanningSensitivity(float panningSensitivity)
  346. {
  347. m_panningSensitivity = AZ::GetMax(panningSensitivity, 0.0f);
  348. }
  349. void ArcBallControllerComponent::SetZoomingSensitivity(float zoomingSensitivity)
  350. {
  351. m_zoomingSensitivity = AZ::GetMax(zoomingSensitivity, 0.0f);
  352. }
  353. AZ::Vector3 ArcBallControllerComponent::GetCenter()
  354. {
  355. return m_center;
  356. }
  357. AZ::Vector3 ArcBallControllerComponent::GetPan()
  358. {
  359. return m_panningOffset;
  360. }
  361. float ArcBallControllerComponent::GetDistance()
  362. {
  363. return m_distance;
  364. }
  365. float ArcBallControllerComponent::GetMinDistance()
  366. {
  367. return m_minDistance;
  368. }
  369. float ArcBallControllerComponent::GetMaxDistance()
  370. {
  371. return m_maxDistance;
  372. }
  373. float ArcBallControllerComponent::GetHeading()
  374. {
  375. return m_currentHeading;
  376. }
  377. float ArcBallControllerComponent::GetPitch()
  378. {
  379. return m_currentPitch;
  380. }
  381. float ArcBallControllerComponent::GetPanningSensitivity()
  382. {
  383. return m_panningSensitivity;
  384. }
  385. float ArcBallControllerComponent::GetZoomingSensitivity()
  386. {
  387. return m_zoomingSensitivity;
  388. }
  389. } // namespace Debug
  390. } // namespace AZ