FlyCameraInputComponent.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479
  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 "FlyCameraInputComponent.h"
  9. #include <ISystem.h>
  10. #include <IConsole.h>
  11. #include <AzCore/Component/Component.h>
  12. #include <AzCore/Component/TransformBus.h>
  13. #include <AzCore/Math/Quaternion.h>
  14. #include <AzCore/Math/Transform.h>
  15. #include <AzCore/Serialization/SerializeContext.h>
  16. #include <AzCore/Serialization/EditContext.h>
  17. #include <AzCore/RTTI/BehaviorContext.h>
  18. #include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
  19. #include <AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.h>
  20. #include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
  21. #include <AzFramework/Input/Devices/Touch/InputDeviceTouch.h>
  22. #include <MathConversion.h>
  23. #include <Atom/RPI.Public/ViewProviderBus.h>
  24. #include <Atom/RPI.Public/View.h>
  25. #include <AzFramework/Components/CameraBus.h>
  26. using namespace AzFramework;
  27. using namespace AZ::AtomBridge;
  28. //////////////////////////////////////////////////////////////////////////////
  29. namespace
  30. {
  31. //////////////////////////////////////////////////////////////////////////
  32. int GenerateThumbstickTexture()
  33. {
  34. // [GFX TODO] Get Atom test fly cam virtual thumbsticks working on mobile
  35. return 0;
  36. }
  37. //////////////////////////////////////////////////////////////////////////
  38. void ReleaseThumbstickTexture([[maybe_unused]] int textureId)
  39. {
  40. // [GFX TODO] Get Atom test fly cam virtual thumbsticks working on mobile
  41. }
  42. //////////////////////////////////////////////////////////////////////////
  43. void DrawThumbstick([[maybe_unused]] Vec2 initialPosition,
  44. [[maybe_unused]] Vec2 currentPosition,
  45. [[maybe_unused]] int textureId)
  46. {
  47. // [GFX TODO] Get Atom test fly cam virtual thumbsticks working on mobile
  48. // we do not have any 2D drawing capability like IDraw2d in Atom yet
  49. }
  50. }
  51. //////////////////////////////////////////////////////////////////////////////
  52. const AZ::Crc32 FlyCameraInputComponent::UnknownInputChannelId("unknown_input_channel_id");
  53. //////////////////////////////////////////////////////////////////////////////
  54. void FlyCameraInputComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
  55. {
  56. required.push_back(AZ_CRC_CE("TransformService"));
  57. }
  58. //////////////////////////////////////////////////////////////////////////////
  59. void FlyCameraInputComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
  60. {
  61. provided.push_back(AZ_CRC_CE("InputService"));
  62. }
  63. //////////////////////////////////////////////////////////////////////////////
  64. void FlyCameraInputComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
  65. {
  66. incompatible.push_back(AZ_CRC_CE("NonUniformScaleService"));
  67. }
  68. //////////////////////////////////////////////////////////////////////////////
  69. void FlyCameraInputComponent::Reflect(AZ::ReflectContext* reflection)
  70. {
  71. AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection);
  72. if (serializeContext)
  73. {
  74. serializeContext->Class<FlyCameraInputComponent, AZ::Component>()
  75. ->Version(1)
  76. ->Field("Move Speed", &FlyCameraInputComponent::m_moveSpeed)
  77. ->Field("Rotation Speed", &FlyCameraInputComponent::m_rotationSpeed)
  78. ->Field("Mouse Sensitivity", &FlyCameraInputComponent::m_mouseSensitivity)
  79. ->Field("Invert Rotation Input X", &FlyCameraInputComponent::m_InvertRotationInputAxisX)
  80. ->Field("Invert Rotation Input Y", &FlyCameraInputComponent::m_InvertRotationInputAxisY)
  81. ->Field("Is enabled", &FlyCameraInputComponent::m_isEnabled);
  82. AZ::EditContext* editContext = serializeContext->GetEditContext();
  83. if (editContext)
  84. {
  85. editContext->Class<FlyCameraInputComponent>("Fly Camera Input", "The Fly Camera Input allows you to control the camera")
  86. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  87. ->Attribute("Category", "Gameplay")
  88. ->Attribute("Icon", "Editor/Icons/Components/FlyCameraInput.svg")
  89. ->Attribute("ViewportIcon", "Editor/Icons/Components/Viewport/FlyCameraInput.svg")
  90. ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/gameplay/fly-camera-input/")
  91. ->Attribute("AutoExpand", true)
  92. ->Attribute("AppearsInAddComponentMenu", AZ_CRC_CE("Game"))
  93. ->DataElement(0, &FlyCameraInputComponent::m_moveSpeed, "Move Speed", "Speed at which the camera moves")
  94. ->Attribute("Min", 1.0f)
  95. ->Attribute("Max", 100.0f)
  96. ->Attribute("ChangeNotify", AZ_CRC_CE("RefreshValues"))
  97. ->DataElement(0, &FlyCameraInputComponent::m_rotationSpeed, "Rotation Speed", "Speed at which the camera rotates")
  98. ->Attribute("Min", 1.0f)
  99. ->Attribute("Max", 100.0f)
  100. ->Attribute("ChangeNotify", AZ_CRC_CE("RefreshValues"))
  101. ->DataElement(0, &FlyCameraInputComponent::m_mouseSensitivity, "Mouse Sensitivity", "Mouse sensitivity factor")
  102. ->Attribute("Min", 0.0f)
  103. ->Attribute("Max", 1.0f)
  104. ->Attribute("ChangeNotify", AZ_CRC_CE("RefreshValues"))
  105. ->DataElement(0, &FlyCameraInputComponent::m_InvertRotationInputAxisX, "Invert Rotation Input X", "Invert rotation input x-axis")
  106. ->Attribute("ChangeNotify", AZ_CRC_CE("RefreshValues"))
  107. ->DataElement(0, &FlyCameraInputComponent::m_InvertRotationInputAxisY, "Invert Rotation Input Y", "Invert rotation input y-axis")
  108. ->Attribute("ChangeNotify", AZ_CRC_CE("RefreshValues"))
  109. ->DataElement(AZ::Edit::UIHandlers::CheckBox, &FlyCameraInputComponent::m_isEnabled,
  110. "Is Initially Enabled", "When checked, the fly cam input is enabled on activate, else it has to be specifically enabled.");
  111. }
  112. }
  113. AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(reflection);
  114. if (behaviorContext)
  115. {
  116. behaviorContext->EBus<FlyCameraInputBus>("FlyCameraInputBus")
  117. ->Event("SetIsEnabled", &FlyCameraInputBus::Events::SetIsEnabled)
  118. ->Event("GetIsEnabled", &FlyCameraInputBus::Events::GetIsEnabled);
  119. }
  120. }
  121. //////////////////////////////////////////////////////////////////////////////
  122. FlyCameraInputComponent::~FlyCameraInputComponent()
  123. {
  124. ReleaseThumbstickTexture(m_thumbstickTextureId);
  125. }
  126. //////////////////////////////////////////////////////////////////////////////
  127. void FlyCameraInputComponent::Init()
  128. {
  129. m_thumbstickTextureId = GenerateThumbstickTexture();
  130. }
  131. //////////////////////////////////////////////////////////////////////////////
  132. void FlyCameraInputComponent::Activate()
  133. {
  134. InputChannelEventListener::Connect();
  135. AZ::TickBus::Handler::BusConnect();
  136. FlyCameraInputBus::Handler::BusConnect(GetEntityId());
  137. }
  138. //////////////////////////////////////////////////////////////////////////////
  139. void FlyCameraInputComponent::Deactivate()
  140. {
  141. FlyCameraInputBus::Handler::BusDisconnect();
  142. AZ::TickBus::Handler::BusDisconnect();
  143. InputChannelEventListener::Disconnect();
  144. }
  145. //////////////////////////////////////////////////////////////////////////////
  146. void FlyCameraInputComponent::OnTick(float deltaTime, AZ::ScriptTimePoint /*time*/)
  147. {
  148. if (!m_isEnabled)
  149. {
  150. return;
  151. }
  152. AZ::Transform worldTransform = AZ::Transform::Identity();
  153. AZ::TransformBus::EventResult(worldTransform, GetEntityId(), &AZ::TransformBus::Events::GetWorldTM);
  154. // Update movement
  155. const float moveSpeed = m_moveSpeed * deltaTime;
  156. const AZ::Vector3 right = worldTransform.GetBasisX();
  157. const AZ::Vector3 forward = worldTransform.GetBasisY();
  158. const AZ::Vector3 up = worldTransform.GetBasisZ();
  159. const AZ::Vector3 movement = (forward * m_movement.y) + (right * m_movement.x) + (up * m_movement.z);
  160. const AZ::Vector3 newPosition = worldTransform.GetTranslation() + (movement * moveSpeed);
  161. worldTransform.SetTranslation(newPosition);
  162. const Vec2 invertedRotation(m_InvertRotationInputAxisX ? m_rotation.x : -m_rotation.x,
  163. m_InvertRotationInputAxisY ? m_rotation.y : -m_rotation.y);
  164. // Update rotation (not sure how to do this properly using just AZ::Quaternion)
  165. const AZ::Quaternion worldOrientation = worldTransform.GetRotation();
  166. const Ang3 rotation(AZQuaternionToLYQuaternion(worldOrientation));
  167. const Ang3 newRotation = rotation + Ang3(DEG2RAD(invertedRotation.y), 0.f, DEG2RAD(invertedRotation.x)) * m_rotationSpeed;
  168. const AZ::Quaternion newOrientation = LYQuaternionToAZQuaternion(Quat(newRotation));
  169. worldTransform.SetRotation(newOrientation);
  170. AZ::TransformBus::Event(GetEntityId(), &AZ::TransformBus::Events::SetWorldTM, worldTransform);
  171. }
  172. ////////////////////////////////////////////////////////////////////////////////////////////////////
  173. bool FlyCameraInputComponent::OnInputChannelEventFiltered(const InputChannel& inputChannel)
  174. {
  175. if (!m_isEnabled)
  176. {
  177. return false;
  178. }
  179. const InputDeviceId& deviceId = inputChannel.GetInputDevice().GetInputDeviceId();
  180. if (InputDeviceMouse::IsMouseDevice(deviceId))
  181. {
  182. OnMouseEvent(inputChannel);
  183. }
  184. else if (InputDeviceKeyboard::IsKeyboardDevice(deviceId))
  185. {
  186. OnKeyboardEvent(inputChannel);
  187. }
  188. else if (InputDeviceTouch::IsTouchDevice(deviceId))
  189. {
  190. const InputChannel::PositionData2D* positionData2D = inputChannel.GetCustomData<InputChannel::PositionData2D>();
  191. if (positionData2D)
  192. {
  193. float defaultViewWidth = GetViewWidth();
  194. float defaultViewHeight = GetViewHeight();
  195. const Vec2 screenPosition(positionData2D->m_normalizedPosition.GetX() * defaultViewWidth,
  196. positionData2D->m_normalizedPosition.GetY() * defaultViewHeight);
  197. OnTouchEvent(inputChannel, screenPosition);
  198. }
  199. }
  200. else if (AzFramework::InputDeviceGamepad::IsGamepadDevice(deviceId))
  201. {
  202. OnGamepadEvent(inputChannel);
  203. }
  204. return false;
  205. }
  206. ////////////////////////////////////////////////////////////////////////////////////////////////////
  207. void FlyCameraInputComponent::SetIsEnabled(bool isEnabled)
  208. {
  209. m_isEnabled = isEnabled;
  210. }
  211. ////////////////////////////////////////////////////////////////////////////////////////////////////
  212. bool FlyCameraInputComponent::GetIsEnabled()
  213. {
  214. return m_isEnabled;
  215. }
  216. ////////////////////////////////////////////////////////////////////////////////////////////////////
  217. float Snap_s360(float val)
  218. {
  219. if (val < 0.0f)
  220. {
  221. val = f32(360.0f + fmodf(val, 360.0f));
  222. }
  223. else if (val >= 360.0f)
  224. {
  225. val = f32(fmodf(val, 360.0f));
  226. }
  227. return val;
  228. }
  229. ////////////////////////////////////////////////////////////////////////////////////////////////////
  230. void FlyCameraInputComponent::OnMouseEvent(const InputChannel& inputChannel)
  231. {
  232. const InputChannelId& channelId = inputChannel.GetInputChannelId();
  233. if (channelId == InputDeviceMouse::Movement::X)
  234. {
  235. m_rotation.x = Snap_s360(inputChannel.GetValue() * m_mouseSensitivity);
  236. }
  237. else if (channelId == InputDeviceMouse::Movement::Y)
  238. {
  239. m_rotation.y = Snap_s360(inputChannel.GetValue() * m_mouseSensitivity);
  240. }
  241. }
  242. ////////////////////////////////////////////////////////////////////////////////////////////////////
  243. void FlyCameraInputComponent::OnKeyboardEvent(const InputChannel& inputChannel)
  244. {
  245. if (gEnv && gEnv->pConsole && gEnv->pConsole->IsOpened())
  246. {
  247. return;
  248. }
  249. const InputChannelId& channelId = inputChannel.GetInputChannelId();
  250. if (channelId == InputDeviceKeyboard::Key::AlphanumericW)
  251. {
  252. m_movement.y = inputChannel.GetValue();
  253. }
  254. if (channelId == InputDeviceKeyboard::Key::AlphanumericA)
  255. {
  256. m_movement.x = -inputChannel.GetValue();
  257. }
  258. if (channelId == InputDeviceKeyboard::Key::AlphanumericS)
  259. {
  260. m_movement.y = -inputChannel.GetValue();
  261. }
  262. if (channelId == InputDeviceKeyboard::Key::AlphanumericD)
  263. {
  264. m_movement.x = inputChannel.GetValue();
  265. }
  266. if(channelId == InputDeviceKeyboard::Key::AlphanumericE)
  267. {
  268. m_movement.z = inputChannel.GetValue();
  269. }
  270. if(channelId == InputDeviceKeyboard::Key::AlphanumericQ)
  271. {
  272. m_movement.z = -inputChannel.GetValue();
  273. }
  274. }
  275. ////////////////////////////////////////////////////////////////////////////////////////////////////
  276. void FlyCameraInputComponent::OnGamepadEvent(const InputChannel& inputChannel)
  277. {
  278. const InputChannelId& channelId = inputChannel.GetInputChannelId();
  279. if (channelId == InputDeviceGamepad::ThumbStickAxis1D::LX)
  280. {
  281. m_movement.x = inputChannel.GetValue();
  282. }
  283. if (channelId == InputDeviceGamepad::ThumbStickAxis1D::LY)
  284. {
  285. m_movement.y = inputChannel.GetValue();
  286. }
  287. if (channelId == InputDeviceGamepad::Trigger::L2)
  288. {
  289. m_movement.z = -inputChannel.GetValue();
  290. }
  291. if (channelId == InputDeviceGamepad::Trigger::R2)
  292. {
  293. m_movement.z = inputChannel.GetValue();
  294. }
  295. if (channelId == InputDeviceGamepad::ThumbStickAxis1D::RX)
  296. {
  297. m_rotation.x = inputChannel.GetValue();
  298. }
  299. if (channelId == InputDeviceGamepad::ThumbStickAxis1D::RY)
  300. {
  301. m_rotation.y = inputChannel.GetValue();
  302. }
  303. }
  304. ////////////////////////////////////////////////////////////////////////////////////////////////////
  305. void FlyCameraInputComponent::OnTouchEvent(const InputChannel& inputChannel, const Vec2& screenPosition)
  306. {
  307. if (inputChannel.IsStateBegan())
  308. {
  309. const float screenCentreX = GetViewWidth() * 0.5f;
  310. if (screenPosition.x <= screenCentreX)
  311. {
  312. if (m_leftFingerId == UnknownInputChannelId)
  313. {
  314. // Initiate left thumb-stick (movement)
  315. m_leftDownPosition = screenPosition;
  316. m_leftFingerId = inputChannel.GetInputChannelId().GetNameCrc32();
  317. DrawThumbstick(m_leftDownPosition, screenPosition, m_thumbstickTextureId);
  318. }
  319. }
  320. else
  321. {
  322. if (m_rightFingerId == UnknownInputChannelId)
  323. {
  324. // Initiate right thumb-stick (rotation)
  325. m_rightDownPosition = screenPosition;
  326. m_rightFingerId = inputChannel.GetInputChannelId().GetNameCrc32();
  327. DrawThumbstick(m_rightDownPosition, screenPosition, m_thumbstickTextureId);
  328. }
  329. }
  330. }
  331. else if (inputChannel.GetInputChannelId().GetNameCrc32() == m_leftFingerId)
  332. {
  333. // Update left thumb-stick (movement)
  334. OnVirtualLeftThumbstickEvent(inputChannel, screenPosition);
  335. }
  336. else if (inputChannel.GetInputChannelId().GetNameCrc32() == m_rightFingerId)
  337. {
  338. // Update right thumb-stick (rotation)
  339. OnVirtualRightThumbstickEvent(inputChannel, screenPosition);
  340. }
  341. }
  342. ////////////////////////////////////////////////////////////////////////////////////////////////////
  343. void FlyCameraInputComponent::OnVirtualLeftThumbstickEvent(const InputChannel& inputChannel, const Vec2& screenPosition)
  344. {
  345. if (inputChannel.GetInputChannelId().GetNameCrc32() != m_leftFingerId)
  346. {
  347. return;
  348. }
  349. switch (inputChannel.GetState())
  350. {
  351. case InputChannel::State::Ended:
  352. {
  353. // Stop movement
  354. m_leftFingerId = UnknownInputChannelId;
  355. m_movement = ZERO;
  356. }
  357. break;
  358. case InputChannel::State::Updated:
  359. {
  360. // Calculate movement
  361. const float discRadius = GetViewWidth() * m_virtualThumbstickRadiusAsPercentageOfScreenWidth;
  362. const float distScalar = 1.0f / discRadius;
  363. Vec2 dist = screenPosition - m_leftDownPosition;
  364. dist *= distScalar;
  365. m_movement.x = AZ::GetClamp(dist.x, -1.0f, 1.0f);
  366. m_movement.y = AZ::GetClamp(-dist.y, -1.0f, 1.0f);
  367. DrawThumbstick(m_leftDownPosition, screenPosition, m_thumbstickTextureId);
  368. }
  369. break;
  370. }
  371. }
  372. ////////////////////////////////////////////////////////////////////////////////////////////////////
  373. void FlyCameraInputComponent::OnVirtualRightThumbstickEvent(const InputChannel& inputChannel, const Vec2& screenPosition)
  374. {
  375. if (inputChannel.GetInputChannelId().GetNameCrc32() != m_rightFingerId)
  376. {
  377. return;
  378. }
  379. switch (inputChannel.GetState())
  380. {
  381. case InputChannel::State::Ended:
  382. {
  383. // Stop rotation
  384. m_rightFingerId = UnknownInputChannelId;
  385. m_rotation = ZERO;
  386. }
  387. break;
  388. case InputChannel::State::Updated:
  389. {
  390. // Calculate rotation
  391. const float discRadius = GetViewWidth() * m_virtualThumbstickRadiusAsPercentageOfScreenWidth;
  392. const float distScalar = 1.0f / discRadius;
  393. Vec2 dist = screenPosition - m_rightDownPosition;
  394. dist *= distScalar;
  395. m_rotation.x = AZ::GetClamp(dist.x, -1.0f, 1.0f);
  396. m_rotation.y = AZ::GetClamp(dist.y, -1.0f, 1.0f);
  397. DrawThumbstick(m_rightDownPosition, screenPosition, m_thumbstickTextureId);
  398. }
  399. break;
  400. }
  401. }
  402. ////////////////////////////////////////////////////////////////////////////////////////////////////
  403. float FlyCameraInputComponent::GetViewWidth() const
  404. {
  405. float viewWidth = 256.0f;
  406. Camera::CameraRequestBus::EventResult(viewWidth, GetEntityId(), &Camera::CameraRequestBus::Events::GetFrustumWidth);
  407. return viewWidth;
  408. }
  409. ////////////////////////////////////////////////////////////////////////////////////////////////////
  410. float FlyCameraInputComponent::GetViewHeight() const
  411. {
  412. float viewHeight = 256.0f;
  413. Camera::CameraRequestBus::EventResult(viewHeight, GetEntityId(), &Camera::CameraRequestBus::Events::GetFrustumHeight);
  414. return viewHeight;
  415. }