NoClipControllerComponent.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  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/NoClipControllerComponent.h>
  9. #include <AzCore/Component/TransformBus.h>
  10. #include <AzCore/Serialization/SerializeContext.h>
  11. #include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
  12. #include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
  13. #include <AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.h>
  14. #include <AzFramework/Input/Devices/Touch/InputDeviceTouch.h>
  15. #include <AzFramework/Components/CameraBus.h>
  16. #include <DebugCameraUtils.h>
  17. namespace AZ
  18. {
  19. namespace Debug
  20. {
  21. const AzFramework::InputChannelId NoClipControllerComponent::TouchEvent::InvalidTouchChannelId = AzFramework::InputChannelId("InvalidChannel");
  22. static constexpr float MaxFov = 160.0f * Constants::Pi / 180.0f;
  23. static constexpr float MinFov = 1.0f * Constants::Pi / 180.0f;
  24. static constexpr float DefaultFov = Constants::QuarterPi;
  25. static constexpr float deadZone = 0.07f;
  26. void NoClipControllerProperties::Reflect(AZ::ReflectContext* context)
  27. {
  28. if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  29. {
  30. serializeContext->Class<NoClipControllerProperties>()
  31. ->Version(2)
  32. ->Field("Mouse Sensitivity X", &NoClipControllerProperties::m_mouseSensitivityX)
  33. ->Field("Mouse Sensitivity Y", &NoClipControllerProperties::m_mouseSensitivityY)
  34. ->Field("Move Speed", &NoClipControllerProperties::m_moveSpeed)
  35. ->Field("Panning Speed", &NoClipControllerProperties::m_panningSpeed)
  36. ->Field("Touch Sensitivity", &NoClipControllerProperties::m_touchSensitivity);
  37. }
  38. }
  39. NoClipControllerComponent::NoClipControllerComponent()
  40. : AzFramework::InputChannelEventListener(AzFramework::InputChannelEventListener::GetPriorityDefault())
  41. {}
  42. void NoClipControllerComponent::Reflect(AZ::ReflectContext* reflection)
  43. {
  44. NoClipControllerProperties::Reflect(reflection);
  45. if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection))
  46. {
  47. serializeContext->Class<NoClipControllerComponent, CameraControllerComponent, AZ::Component>()
  48. ->Version(1)
  49. ->Field("Properties", &NoClipControllerComponent::m_properties);
  50. }
  51. }
  52. void NoClipControllerComponent::OnEnabled()
  53. {
  54. // Reset parameters
  55. m_mouseLookEnabled = false;
  56. m_inputStates = 0;
  57. m_currentHeading = 0.0f;
  58. m_currentPitch = 0.0f;
  59. m_currentFov = DefaultFov;
  60. m_lastForward = 0.0f;
  61. m_lastStrafe = 0.0f;
  62. m_lastAscent = 0.0f;
  63. NoClipControllerRequestBus::Handler::BusConnect(GetEntityId());
  64. AzFramework::InputChannelEventListener::Connect();
  65. AZ::TickBus::Handler::BusConnect();
  66. }
  67. void NoClipControllerComponent::OnDisabled()
  68. {
  69. TickBus::Handler::BusDisconnect();
  70. AzFramework::InputChannelEventListener::Disconnect();
  71. NoClipControllerRequestBus::Handler::BusDisconnect();
  72. // Reset the Fov to default.
  73. Camera::CameraRequestBus::Event(GetEntityId(), &Camera::CameraRequestBus::Events::SetFovRadians, DefaultFov);
  74. }
  75. void NoClipControllerComponent::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
  76. {
  77. static const float normalSpeed = 3.0f;
  78. static const float sprintSpeed = 10.0f;
  79. float speedFactor = m_inputStates[CameraKeys::FastMode] ? sprintSpeed : normalSpeed;
  80. float forward = m_properties.m_moveSpeed * speedFactor * (
  81. (m_inputStates[CameraKeys::Forward] ? deltaTime : 0.0f) +
  82. (m_inputStates[CameraKeys::Back] ? -deltaTime : 0.0f));
  83. float strafe = m_properties.m_panningSpeed * speedFactor * (
  84. (m_inputStates[CameraKeys::Right] ? deltaTime : 0.0f) +
  85. (m_inputStates[CameraKeys::Left] ? -deltaTime : 0.0f));
  86. float ascent = m_properties.m_panningSpeed * speedFactor * (
  87. (m_inputStates[CameraKeys::Up] ? deltaTime : 0.0f) +
  88. (m_inputStates[CameraKeys::Down] ? -deltaTime : 0.0f));
  89. ApplyMomentum(m_lastForward, forward, deltaTime);
  90. ApplyMomentum(m_lastStrafe, strafe, deltaTime);
  91. ApplyMomentum(m_lastAscent, ascent, deltaTime);
  92. AZ::Vector3 worldPosition;
  93. AZ::TransformBus::EventResult(
  94. worldPosition, GetEntityId(), &AZ::TransformBus::Events::GetWorldTranslation);
  95. // The coordinate system is right-handed and Z-up. So heading is a rotation around the Z axis.
  96. // After that rotation we rotate around the (rotated by heading) X axis for pitch.
  97. AZ::Quaternion orientation = AZ::Quaternion::CreateRotationZ(m_currentHeading)
  98. * AZ::Quaternion::CreateRotationX(m_currentPitch);
  99. AZ::Vector3 position = orientation.TransformVector(AZ::Vector3(strafe, forward, ascent)) + worldPosition;
  100. AZ::Transform transform = AZ::Transform::CreateFromQuaternionAndTranslation(orientation, position);
  101. AZ::TransformBus::Event(
  102. GetEntityId(), &AZ::TransformBus::Events::SetWorldTM, transform);
  103. Camera::CameraRequestBus::Event(GetEntityId(), &Camera::CameraRequestBus::Events::SetFovRadians, m_currentFov);
  104. }
  105. bool NoClipControllerComponent::OnInputChannelEventFiltered(const AzFramework::InputChannel& inputChannel)
  106. {
  107. static const auto KeyCount = static_cast<uint32_t>(CameraKeys::Count);
  108. static const float PixelToDegree = 1.0 / 360.0f;
  109. static const AzFramework::InputChannelId CameraInputMap[KeyCount] =
  110. {
  111. AzFramework::InputDeviceKeyboard::Key::AlphanumericW, // Forward
  112. AzFramework::InputDeviceKeyboard::Key::AlphanumericS, // Back
  113. AzFramework::InputDeviceKeyboard::Key::AlphanumericA, // Left
  114. AzFramework::InputDeviceKeyboard::Key::AlphanumericD, // Right
  115. AzFramework::InputDeviceKeyboard::Key::AlphanumericQ, // Up
  116. AzFramework::InputDeviceKeyboard::Key::AlphanumericE, // Down
  117. AzFramework::InputDeviceKeyboard::Key::ModifierShiftL, // FastMode
  118. };
  119. static const AzFramework::InputChannelId CameraGamepadInputMap[KeyCount] =
  120. {
  121. AzFramework::InputDeviceGamepad::Button::DU, // Forward
  122. AzFramework::InputDeviceGamepad::Button::DD, // Back
  123. AzFramework::InputDeviceGamepad::Button::DL, // Left
  124. AzFramework::InputDeviceGamepad::Button::DR, // Right
  125. AzFramework::InputDeviceGamepad::Button::R1, // Up
  126. AzFramework::InputDeviceGamepad::Button::L1, // Down
  127. AzFramework::InputDeviceGamepad::Trigger::R2, // FastMode
  128. };
  129. uint32_t handledChannels = NoClipControllerChannel_None;
  130. const AzFramework::InputChannelId& inputChannelId = inputChannel.GetInputChannelId();
  131. switch (inputChannel.GetState())
  132. {
  133. case AzFramework::InputChannel::State::Began:
  134. case AzFramework::InputChannel::State::Updated: // update the camera rotation
  135. {
  136. //Keyboard & Mouse
  137. if (m_mouseLookEnabled && inputChannelId == AzFramework::InputDeviceMouse::Movement::X)
  138. {
  139. // modify yaw angle
  140. m_currentHeading -= inputChannel.GetValue() * m_properties.m_mouseSensitivityX * PixelToDegree;
  141. m_currentHeading = NormalizeAngle(m_currentHeading);
  142. }
  143. else if (m_mouseLookEnabled && inputChannelId == AzFramework::InputDeviceMouse::Movement::Y)
  144. {
  145. // modify pitch angle
  146. m_currentPitch -= inputChannel.GetValue() * m_properties.m_mouseSensitivityY * PixelToDegree;
  147. m_currentPitch = AZStd::max(m_currentPitch, -AZ::Constants::HalfPi);
  148. m_currentPitch = AZStd::min(m_currentPitch, AZ::Constants::HalfPi);
  149. }
  150. else if (inputChannelId == AzFramework::InputDeviceMouse::Movement::Z)
  151. {
  152. // modify field of view
  153. m_currentFov = GetClamp(m_currentFov - inputChannel.GetValue() * 0.0005f * m_currentFov, MinFov, MaxFov);
  154. handledChannels |= NoClipControllerChannel_Fov;
  155. }
  156. else if (inputChannelId == AzFramework::InputDeviceMouse::Button::Right)
  157. {
  158. m_mouseLookEnabled = true;
  159. handledChannels |= NoClipControllerChannel_Orientation;
  160. }
  161. else
  162. {
  163. for (uint32_t i = 0; i < KeyCount; ++i)
  164. {
  165. if (inputChannelId == CameraInputMap[i])
  166. {
  167. m_inputStates[i] = true;
  168. if (i != CameraKeys::FastMode)
  169. {
  170. handledChannels |= NoClipControllerChannel_Position;
  171. }
  172. break;
  173. }
  174. }
  175. }
  176. // Gamepad
  177. if (inputChannelId == AzFramework::InputDeviceGamepad::Trigger::L2)
  178. {
  179. m_mouseLookEnabled = true;
  180. handledChannels |= NoClipControllerChannel_Orientation;
  181. }
  182. else if (m_mouseLookEnabled)
  183. {
  184. if (inputChannelId == AzFramework::InputDeviceGamepad::ThumbStickAxis1D::RX)
  185. {
  186. // modify yaw angle
  187. m_currentHeading -= inputChannel.GetValue() * m_properties.m_mouseSensitivityX * PixelToDegree;
  188. m_currentHeading = NormalizeAngle(m_currentHeading);
  189. }
  190. else if (inputChannelId == AzFramework::InputDeviceGamepad::ThumbStickAxis1D::RY)
  191. {
  192. // modify pitch angle
  193. m_currentPitch += inputChannel.GetValue() * m_properties.m_mouseSensitivityY * PixelToDegree;
  194. m_currentPitch = AZStd::max(m_currentPitch, -AZ::Constants::HalfPi);
  195. m_currentPitch = AZStd::min(m_currentPitch, AZ::Constants::HalfPi);
  196. }
  197. for (uint32_t i = 0; i < KeyCount; ++i)
  198. {
  199. if (inputChannelId == CameraGamepadInputMap[i])
  200. {
  201. m_inputStates[i] = true;
  202. if (i != CameraKeys::FastMode)
  203. {
  204. handledChannels |= NoClipControllerChannel_Position;
  205. }
  206. break;
  207. }
  208. }
  209. }
  210. // Touch controls works like two virtual joysticks.
  211. // The left "joystick" controls forward/backward/left and right movements.
  212. // The right "joystick" controls the camera heading and pitch.
  213. // There's no control to move up and down.
  214. if (inputChannelId == AzFramework::InputDeviceTouch::Touch::Index0 || inputChannelId == AzFramework::InputDeviceTouch::Touch::Index1)
  215. {
  216. auto* positionData = inputChannel.GetCustomData<AzFramework::InputChannel::PositionData2D>();
  217. AZ::Vector2 screenPos = positionData->m_normalizedPosition;
  218. if (inputChannelId == m_mouseLookTouch.m_channelId)
  219. {
  220. // modify yaw angle
  221. AZ::Vector2 deltaPos = screenPos - m_mouseLookTouch.m_initialPos;
  222. AZ::Vector2 inputValue = AZ::Vector2(fabsf(deltaPos.GetX()) > deadZone ? 1.0f : 0, fabsf(deltaPos.GetY()) > deadZone ? 1.0f : 0);
  223. m_currentHeading -= inputValue.GetX() * AZ::GetSign(deltaPos.GetX()) * m_properties.m_touchSensitivity * m_properties.m_mouseSensitivityX * PixelToDegree;
  224. m_currentHeading = NormalizeAngle(m_currentHeading);
  225. // modify pitch angle
  226. m_currentPitch -= inputValue.GetY() * AZ::GetSign(deltaPos.GetY()) * m_properties.m_touchSensitivity * m_properties.m_mouseSensitivityY * PixelToDegree;
  227. m_currentPitch = AZStd::max(m_currentPitch, -AZ::Constants::HalfPi);
  228. m_currentPitch = AZStd::min(m_currentPitch, AZ::Constants::HalfPi);
  229. handledChannels |= NoClipControllerChannel_Orientation;
  230. }
  231. else if (inputChannelId == m_movementTouch.m_channelId)
  232. {
  233. AZ::Vector2 deltaPos = screenPos - m_movementTouch.m_initialPos;
  234. m_inputStates[Forward] = deltaPos.GetY() < -deadZone ? true : false;
  235. m_inputStates[Back] = deltaPos.GetY() > deadZone ? true : false;
  236. m_inputStates[Left] = deltaPos.GetX() < -deadZone ? true : false;
  237. m_inputStates[Right] = deltaPos.GetX() > deadZone ? true : false;
  238. handledChannels |= NoClipControllerChannel_Position;
  239. }
  240. else
  241. {
  242. bool isMouseLook = (screenPos.GetX() > 0.5);
  243. auto& touchEvent = isMouseLook ? m_mouseLookTouch : m_movementTouch;
  244. if (touchEvent.m_channelId == TouchEvent::InvalidTouchChannelId)
  245. {
  246. touchEvent.m_channelId = inputChannelId;
  247. touchEvent.m_initialPos = screenPos;
  248. }
  249. if (isMouseLook)
  250. {
  251. handledChannels |= NoClipControllerChannel_Orientation;
  252. }
  253. else
  254. {
  255. handledChannels |= NoClipControllerChannel_Position;
  256. }
  257. }
  258. }
  259. if (handledChannels && AzFramework::InputChannel::State::Began == inputChannel.GetState())
  260. {
  261. CameraControllerNotificationBus::Broadcast(&CameraControllerNotifications::OnCameraMoveBegan, RTTI_GetType(), handledChannels);
  262. }
  263. break;
  264. }
  265. case AzFramework::InputChannel::State::Ended: // update the released input state
  266. {
  267. if (inputChannelId == AzFramework::InputDeviceMouse::Button::Right)
  268. {
  269. m_mouseLookEnabled = false;
  270. handledChannels |= NoClipControllerChannel_Orientation;
  271. }
  272. else if (inputChannelId == AzFramework::InputDeviceMouse::Movement::Z)
  273. {
  274. handledChannels |= NoClipControllerChannel_Fov;
  275. }
  276. else if (inputChannelId == AzFramework::InputDeviceGamepad::Trigger::L2)
  277. {
  278. m_mouseLookEnabled = false;
  279. handledChannels |= NoClipControllerChannel_Orientation;
  280. // On gamepads, Trigger::L2 also indicates positional movement, see above.
  281. handledChannels |= NoClipControllerChannel_Position;
  282. }
  283. else if (inputChannelId == m_movementTouch.m_channelId)
  284. {
  285. m_movementTouch.m_channelId = TouchEvent::InvalidTouchChannelId;
  286. for (uint32_t i = 0; i < KeyCount; ++i)
  287. {
  288. m_inputStates[i] = false;
  289. if (i != CameraKeys::FastMode)
  290. {
  291. handledChannels |= NoClipControllerChannel_Position;
  292. }
  293. }
  294. }
  295. else if (inputChannelId == m_mouseLookTouch.m_channelId)
  296. {
  297. m_mouseLookTouch.m_channelId = TouchEvent::InvalidTouchChannelId;
  298. handledChannels |= NoClipControllerChannel_Orientation;
  299. }
  300. else
  301. {
  302. for (uint32_t i = 0; i < KeyCount; ++i)
  303. {
  304. if (inputChannelId == CameraInputMap[i] || inputChannelId == CameraGamepadInputMap[i])
  305. {
  306. m_inputStates[i] = false;
  307. if (i != CameraKeys::FastMode)
  308. {
  309. handledChannels |= NoClipControllerChannel_Position;
  310. }
  311. break;
  312. }
  313. }
  314. }
  315. if (handledChannels)
  316. {
  317. CameraControllerNotificationBus::Broadcast(&CameraControllerNotifications::OnCameraMoveEnded, RTTI_GetType(), handledChannels);
  318. }
  319. break;
  320. }
  321. default:
  322. {
  323. break;
  324. }
  325. }
  326. return false;
  327. }
  328. void NoClipControllerComponent::SetMouseSensitivityX(float mouseSensitivityX)
  329. {
  330. m_properties.m_mouseSensitivityX = mouseSensitivityX;
  331. }
  332. void NoClipControllerComponent::SetMouseSensitivityY(float mouseSensitivityY)
  333. {
  334. m_properties.m_mouseSensitivityY = mouseSensitivityY;
  335. }
  336. void NoClipControllerComponent::SetMoveSpeed(float moveSpeed)
  337. {
  338. m_properties.m_moveSpeed = moveSpeed;
  339. }
  340. void NoClipControllerComponent::SetPanningSpeed(float panningSpeed)
  341. {
  342. m_properties.m_panningSpeed = panningSpeed;
  343. }
  344. void NoClipControllerComponent::SetControllerProperties(const NoClipControllerProperties& properties)
  345. {
  346. m_properties = properties;
  347. }
  348. void NoClipControllerComponent::SetTouchSensitivity(float touchSensitivity)
  349. {
  350. m_properties.m_touchSensitivity = touchSensitivity;
  351. }
  352. void NoClipControllerComponent::SetPosition(AZ::Vector3 position)
  353. {
  354. AZ::TransformBus::Event(GetEntityId(), &AZ::TransformBus::Events::SetWorldTranslation, position);
  355. }
  356. void NoClipControllerComponent::SetHeading(float heading)
  357. {
  358. m_currentHeading = heading;
  359. m_currentHeading = NormalizeAngle(m_currentHeading);
  360. }
  361. void NoClipControllerComponent::SetCameraStateForward(float value)
  362. {
  363. m_inputStates[CameraKeys::Forward] = value >= -deadZone;
  364. }
  365. void NoClipControllerComponent::SetCameraStateBack(float value)
  366. {
  367. m_inputStates[Back] = value <= deadZone;
  368. }
  369. void NoClipControllerComponent::SetCameraStateLeft(float value)
  370. {
  371. m_inputStates[Left] = value < -deadZone;
  372. }
  373. void NoClipControllerComponent::SetCameraStateRight(float value)
  374. {
  375. m_inputStates[Right] = value > deadZone;
  376. }
  377. void NoClipControllerComponent::SetCameraStateUp(float value)
  378. {
  379. m_inputStates[Up] = value > deadZone;
  380. }
  381. void NoClipControllerComponent::SetCameraStateDown(float value)
  382. {
  383. m_inputStates[Down] = value > deadZone;
  384. }
  385. void NoClipControllerComponent::SetPitch(float pitch)
  386. {
  387. m_currentPitch = pitch;
  388. m_currentPitch = AZStd::max(m_currentPitch, -AZ::Constants::HalfPi);
  389. m_currentPitch = AZStd::min(m_currentPitch, AZ::Constants::HalfPi);
  390. }
  391. void NoClipControllerComponent::SetFov(float fov)
  392. {
  393. m_currentFov = GetClamp(fov, MinFov, MaxFov);
  394. }
  395. float NoClipControllerComponent::GetMouseSensitivityX()
  396. {
  397. return m_properties.m_mouseSensitivityX;
  398. }
  399. float NoClipControllerComponent::GetMouseSensitivityY()
  400. {
  401. return m_properties.m_mouseSensitivityY;
  402. }
  403. float NoClipControllerComponent::GetMoveSpeed()
  404. {
  405. return m_properties.m_moveSpeed;
  406. }
  407. float NoClipControllerComponent::GetPanningSpeed()
  408. {
  409. return m_properties.m_panningSpeed;
  410. }
  411. float NoClipControllerComponent::GetTouchSensitivity()
  412. {
  413. return m_properties.m_touchSensitivity;
  414. }
  415. NoClipControllerProperties NoClipControllerComponent::GetControllerProperties()
  416. {
  417. return m_properties;
  418. }
  419. AZ::Vector3 NoClipControllerComponent::GetPosition()
  420. {
  421. AZ::Vector3 position;
  422. AZ::TransformBus::EventResult(position, GetEntityId(), &AZ::TransformBus::Events::GetWorldTranslation);
  423. return position;
  424. }
  425. float NoClipControllerComponent::GetHeading()
  426. {
  427. return m_currentHeading;
  428. }
  429. float NoClipControllerComponent::GetPitch()
  430. {
  431. return m_currentPitch;
  432. }
  433. float NoClipControllerComponent::GetFov()
  434. {
  435. return m_currentFov;
  436. }
  437. } // namespace Debug
  438. } // namespace AZ