KrakenEffectorComponent.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  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 "KrakenEffectorComponent.h"
  9. #include "ApplePickingNotifications.h"
  10. #include "Manipulator/ManipulatorRequestBus.h"
  11. #include "PickingStructs.h"
  12. #include <AzCore/Component/Entity.h>
  13. #include <AzCore/Component/ComponentApplicationBus.h>
  14. #include <AzCore/Component/TransformBus.h>
  15. #include <AzCore/Serialization/EditContext.h>
  16. #include <AzCore/Serialization/EditContextConstants.inl>
  17. #include <AzCore/Serialization/SerializeContext.h>
  18. #include <AzFramework/Physics/Common/PhysicsSimulatedBody.h>
  19. #include <AzFramework/Physics/RigidBodyBus.h>
  20. #include <LmbrCentral/Shape/BoxShapeComponentBus.h>
  21. namespace AppleKraken
  22. {
  23. namespace DebugStateTransit
  24. {
  25. static const AZStd::unordered_map<EffectorState, const char*> kMapToString{ { EffectorState::INVALID, "INVALID" },
  26. { EffectorState::IDLE, "IDLE" },
  27. { EffectorState::PREPARED, "PREPARED" },
  28. { EffectorState::PICKING, "PICKING" },
  29. { EffectorState::PICKING_STABILIZE, "PICKING_STABILIZE" },
  30. { EffectorState::RETRIEVING, "RETRIEVING" },
  31. { EffectorState::RETRIEVING_NOSE, "RETRIEVING_NOSE" },
  32. { EffectorState::RETRIEVING_STABILIZE,
  33. "RETRIEVING_STABILIZE" } };
  34. // TODO - this is a debug space for a stub implementation. Proper: a state transition machine with lambdas.
  35. AZStd::string StateTransitionString(EffectorState current, EffectorState next)
  36. {
  37. return AZStd::string::format("state transition %s -> %s\n", kMapToString.at(current), kMapToString.at(next));
  38. }
  39. } // namespace DebugStateTransit
  40. KrakenEffectorComponent::KrakenEffectorComponent()
  41. {
  42. InitializeStateProperties();
  43. }
  44. void KrakenEffectorComponent::Activate()
  45. {
  46. ApplePickingRequestBus::Handler::BusConnect(GetEntityId());
  47. AZ::TickBus::Handler::BusConnect();
  48. m_onTriggerHandleBeginHandler = AzPhysics::SimulatedBodyEvents::OnTriggerEnter::Handler(
  49. [&]([[maybe_unused]] AzPhysics::SimulatedBodyHandle bodyHandle, [[maybe_unused]] const AzPhysics::TriggerEvent& event)
  50. {
  51. const AZ::EntityId& e1 = event.m_otherBody->GetEntityId();
  52. const AZ::EntityId& e2 = event.m_triggerBody->GetEntityId();
  53. [[maybe_unused]] const AZ::EntityId& collideToEntityId = m_appleProbe == e1 ? e2 : e1;
  54. // AZStd::string entity_name;
  55. // AZ::ComponentApplicationBus::BroadcastResult(entity_name,
  56. // &AZ::ComponentApplicationRequests::GetEntityName, collideToEntityId);
  57. // AZ_Printf("m_onTriggerHandleBeginHandler", "Collission %s\n", entity_name.c_str());
  58. // AzPhysics::SimulatedBody* collideToEntityId = this->GetEntityId() == e1 ? event.m_triggerBody : event.m_otherBody;}
  59. if (m_currentTask.m_appleEntityId == collideToEntityId)
  60. {
  61. AZ_TracePrintf("m_onTriggerHandleBeginHandler", " %s : m_onTriggerHandle to Apple!====================",
  62. GetEntity()->GetName().c_str());
  63. ApplePickingNotificationBus::Event(this->GetEntityId(),&ApplePickingNotifications::ApplePicked);
  64. if (m_effectorState == EffectorState::PICKING)
  65. {
  66. // start picking the apple
  67. BeginTransitionIfAcceptable(EffectorState::PICKING_STABILIZE);
  68. }
  69. }
  70. if (m_restEntityId == collideToEntityId)
  71. {
  72. AZ_TracePrintf("m_onTriggerHandleBeginHandler", "%s : m_onTriggerHandle to Rest!====================",
  73. GetEntity()->GetName().c_str());
  74. if (m_effectorState == EffectorState::RETRIEVING || m_effectorState == EffectorState::RETRIEVING_NOSE )
  75. {
  76. // start picking the apple
  77. BeginTransitionIfAcceptable(EffectorState::RETRIEVING_STABILIZE);
  78. }
  79. }
  80. });
  81. }
  82. void KrakenEffectorComponent::Deactivate()
  83. {
  84. AZ::TickBus::Handler::BusDisconnect();
  85. ApplePickingRequestBus::Handler::BusDisconnect();
  86. }
  87. void KrakenEffectorComponent::Reflect(AZ::ReflectContext* context)
  88. {
  89. if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context))
  90. {
  91. serialize->Class<KrakenEffectorComponent, AZ::Component>()
  92. ->Version(4)
  93. ->Field("ReachEntity", &KrakenEffectorComponent::m_reachEntity)
  94. ->Field("ManipulatorEntity", &KrakenEffectorComponent::m_manipulatorEntity)
  95. ->Field("AppleProbe", &KrakenEffectorComponent::m_appleProbe)
  96. ->Field("RootManipulatorFreeze", &KrakenEffectorComponent::m_rootEntityToFreeze)
  97. ->Field("BaseLinkToKinematic", &KrakenEffectorComponent::m_baseLinkToKinematic)
  98. ->Field("RestEntity", &KrakenEffectorComponent::m_restEntityId)
  99. ->Field("RetrieveNoseTime", &KrakenEffectorComponent::m_retrieve_nose_time)
  100. ->Field("PickStabilizeTime", &KrakenEffectorComponent::m_stabilize_time)
  101. ->Field("MaxPickingTime", &KrakenEffectorComponent::m_maxPickingTime);
  102. if (AZ::EditContext* ec = serialize->GetEditContext())
  103. {
  104. ec->Class<KrakenEffectorComponent>("Kraken Effector", "Manipulator component for picking apples")
  105. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  106. ->Attribute(AZ::Edit::Attributes::Category, "AppleKraken")
  107. ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game"))
  108. ->DataElement(
  109. AZ::Edit::UIHandlers::EntityId,
  110. &KrakenEffectorComponent::m_reachEntity,
  111. "Kraken Reach entity",
  112. "Kraken entity with box shape to set reach area")
  113. ->DataElement(
  114. AZ::Edit::UIHandlers::EntityId,
  115. &KrakenEffectorComponent::m_manipulatorEntity,
  116. "Entity with manipulator",
  117. "The entity that has a component handling events from ManipulatorRequestBus")
  118. ->DataElement(
  119. AZ::Edit::UIHandlers::EntityId, &KrakenEffectorComponent::m_appleProbe, "Entity to probe apples", "Sucking collider")
  120. ->DataElement(
  121. AZ::Edit::UIHandlers::EntityId,
  122. &KrakenEffectorComponent::m_rootEntityToFreeze,
  123. "RootManipulatorFreeze",
  124. "RootManipulatorFreeze to freeze during robot movement")
  125. ->DataElement(
  126. AZ::Edit::UIHandlers::EntityId,
  127. &KrakenEffectorComponent::m_baseLinkToKinematic,
  128. "BaseLinkToKinematic",
  129. "BaseLinkToKinematic during manipulator movement")
  130. ->DataElement(
  131. AZ::Edit::UIHandlers::EntityId,
  132. &KrakenEffectorComponent::m_restEntityId,
  133. "ManipulatorRestPoint",
  134. "ManipulatorRestPoint")
  135. ->DataElement(
  136. AZ::Edit::UIHandlers::EntityId,
  137. &KrakenEffectorComponent::m_retrieve_nose_time,
  138. "Nose Retrieve Time",
  139. "Nose Retrieve Time")
  140. ->DataElement(
  141. AZ::Edit::UIHandlers::EntityId,
  142. &KrakenEffectorComponent::m_stabilize_time,
  143. "PickStabilizeTime",
  144. "PickStabilizeTime")
  145. ->DataElement(
  146. AZ::Edit::UIHandlers::EntityId, &KrakenEffectorComponent::m_maxPickingTime, "MaxPickingTime", "MaxPickingTime")
  147. ->Attribute(AZ::Edit::Attributes::AutoExpand, true);
  148. }
  149. }
  150. ManipulatorRequestHandler::Reflect(context);
  151. }
  152. void KrakenEffectorComponent::PrepareForPicking()
  153. {
  154. AZ_TracePrintf("KrakenEffectorComponent", "%s: PrepareForPicking\n", GetEntity()->GetName().c_str());
  155. BeginTransitionIfAcceptable(EffectorState::PREPARED);
  156. }
  157. void KrakenEffectorComponent::PickApple(const PickAppleTask& appleTask)
  158. {
  159. AZ_TracePrintf("KrakenEffectorComponent", "%s: PickApple\n", GetEntity()->GetName().c_str());
  160. // TODO - handle appleTask
  161. m_currentTask = appleTask;
  162. ManipulatorRequestBus::Event(m_manipulatorEntity, &ManipulatorRequest::PickApple, appleTask.m_middle);
  163. BeginTransitionIfAcceptable(EffectorState::PICKING);
  164. }
  165. void KrakenEffectorComponent::FinishPicking()
  166. {
  167. AZ_TracePrintf("KrakenEffectorComponent", "%s : FinishPicking\n", GetEntity()->GetName().c_str());
  168. BeginTransitionIfAcceptable(EffectorState::IDLE);
  169. }
  170. PickingState KrakenEffectorComponent::GetEffectorState()
  171. {
  172. PickingState state;
  173. state.m_effectorState = m_effectorState;
  174. state.m_taskProgress = 0.0f; // TODO
  175. state.m_description = DebugStateTransit::kMapToString.at(m_effectorState);
  176. if (m_currentTask.IsValid())
  177. {
  178. state.m_currentTask = m_currentTask;
  179. }
  180. return state;
  181. }
  182. AZ::Obb KrakenEffectorComponent::GetEffectorReachArea()
  183. {
  184. AZ_TracePrintf("KrakenEffectorComponent", "%s: GetEffectorReachArea\n", GetEntity()->GetName().c_str());
  185. AZ::Obb reachArea;
  186. if (m_reachEntity.IsValid())
  187. {
  188. AZ::Transform targetTM = AZ::Transform::CreateIdentity();
  189. AZ::TransformBus::EventResult(targetTM, m_reachEntity, &AZ::TransformBus::Events::GetWorldTM);
  190. AZ::Vector3 dimensions = AZ::Vector3{ 0.f };
  191. LmbrCentral::BoxShapeComponentRequestsBus::EventResult(
  192. dimensions, m_reachEntity, &LmbrCentral::BoxShapeComponentRequests::GetBoxDimensions);
  193. if (!dimensions.IsZero())
  194. {
  195. AZ_Printf("KrakenEffectorComponent", "OurEffectorReachArea :");
  196. AZ_Printf(
  197. "KrakenEffectorComponent", " local dimensions : %f %f %f", dimensions.GetX(), dimensions.GetY(), dimensions.GetZ());
  198. AZ_Printf(
  199. "KrakenEffectorComponent",
  200. " transform - rot : %f %f %f %f",
  201. targetTM.GetRotation().GetX(),
  202. targetTM.GetRotation().GetY(),
  203. targetTM.GetRotation().GetZ(),
  204. targetTM.GetRotation().GetW());
  205. AZ_Printf(
  206. "KrakenEffectorComponent",
  207. " transform - pos : %f %f %f",
  208. targetTM.GetTranslation().GetX(),
  209. targetTM.GetTranslation().GetY(),
  210. targetTM.GetTranslation().GetZ());
  211. reachArea.SetHalfLengths(dimensions / 2);
  212. reachArea.SetPosition(targetTM.GetTranslation());
  213. reachArea.SetRotation(targetTM.GetRotation());
  214. return reachArea;
  215. }
  216. AZ_Warning(
  217. "KrakenEffectorComponent", true, "Reach entity %s does not have BoxShapeComponent!", m_reachEntity.ToString().c_str());
  218. }
  219. AZ_Warning("KrakenEffectorComponent", true, "GetEffectorReachArea - returning invalid reach");
  220. reachArea.SetHalfLengths(AZ::Vector3{ 0, 0, 0 });
  221. reachArea.SetPosition(AZ::Vector3{ 0, 0, 0 }); /// TODO - get it from entity With box
  222. return reachArea;
  223. }
  224. void KrakenEffectorComponent::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
  225. {
  226. m_currentStateTransitionTime += deltaTime;
  227. GetCurrentStateAction()();
  228. if (m_effectorState == m_effectorTargetState)
  229. { // //TODO - nothing to do in stub version
  230. return;
  231. }
  232. // State transition
  233. AZ_TracePrintf(
  234. "KrakenEffectorComponent", "%s : %s", GetEntity()->GetName().c_str(), DebugStateTransit::StateTransitionString(m_effectorState, m_effectorTargetState).c_str());
  235. m_currentStateTransitionTime = 0.0f;
  236. // Update state
  237. auto transitionAction = GetCurrentTransitionAction();
  238. m_effectorState = m_effectorTargetState;
  239. transitionAction();
  240. if (!m_registeredCallback)
  241. {
  242. auto* physicsSystem = AZ::Interface<AzPhysics::SystemInterface>::Get();
  243. auto* sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get();
  244. auto [physicScene, physicBody] = physicsSystem->FindAttachedBodyHandleFromEntityId(m_appleProbe);
  245. if (physicBody != AzPhysics::InvalidSimulatedBodyHandle && physicScene != AzPhysics::InvalidSceneHandle)
  246. {
  247. AzPhysics::SimulatedBody* simulated_body = sceneInterface->GetSimulatedBodyFromHandle(physicScene, physicBody);
  248. simulated_body->RegisterOnTriggerEnterHandler(m_onTriggerHandleBeginHandler);
  249. m_registeredCallback = true;
  250. }
  251. }
  252. }
  253. void KrakenEffectorComponent::LockManipulator(bool locked)
  254. {
  255. AZStd::vector<AZ::EntityId> descendants;
  256. AZ::TransformBus::EventResult(descendants, m_rootEntityToFreeze, &AZ::TransformBus::Events::GetAllDescendants);
  257. descendants.push_back(m_rootEntityToFreeze);
  258. if (is_manipulator_locked != locked)
  259. {
  260. if (locked) {
  261. AZ_Printf("KrakenEffectorComponent", "Locking : %s\n", GetEntity()->GetName().c_str());
  262. }
  263. else{
  264. AZ_Printf("KrakenEffectorComponent", "Unlocking : %s\n", GetEntity()->GetName().c_str());
  265. }
  266. for (auto& descadant : descendants)
  267. {
  268. if (locked)
  269. {
  270. // Lock manipulator, make base_link not kinematic anymore
  271. Physics::RigidBodyRequestBus::Event(descadant, &Physics::RigidBodyRequestBus::Events::DisablePhysics);
  272. Physics::RigidBodyRequestBus::Event(m_baseLinkToKinematic, &Physics::RigidBodyRequestBus::Events::SetKinematic, false);
  273. }
  274. else
  275. {
  276. // loose manipulator, make base_link kinematic
  277. Physics::RigidBodyRequestBus::Event(descadant, &Physics::RigidBodyRequestBus::Events::EnablePhysics);
  278. Physics::RigidBodyRequestBus::Event(m_baseLinkToKinematic, &Physics::RigidBodyRequestBus::Events::SetKinematic, true);
  279. }
  280. }
  281. is_manipulator_locked = locked;
  282. }
  283. }
  284. bool KrakenEffectorComponent::IsTransitionValid(EffectorState targetState) const
  285. {
  286. AZ_Assert(m_effectorState != EffectorState::INVALID, "Effector is in an invalid state! Unable to access transition properties.");
  287. return m_stateProperties.m_allowedTransitions.contains(AZStd::make_pair(m_effectorState, targetState));
  288. }
  289. bool KrakenEffectorComponent::IsTransitionAcceptable(EffectorState targetState) const
  290. {
  291. if (m_effectorState == EffectorState::PICKING && m_effectorState == EffectorState::PICKING)
  292. {
  293. // allow this non-existing state transition without error
  294. return true;
  295. }
  296. if (m_effectorState != m_effectorTargetState)
  297. {
  298. AZ_Error(
  299. "KrakenEffectorComponent",
  300. false,
  301. "%s: Unable to accept request: currently realizing %s", GetEntity()->GetName().c_str(),
  302. DebugStateTransit::StateTransitionString(m_effectorState, m_effectorTargetState).c_str());
  303. return false;
  304. }
  305. if (!IsTransitionValid(targetState))
  306. {
  307. AZ_Error(
  308. "KrakenEffectorComponent",
  309. false,
  310. "%s: Invalid state transition %s",GetEntity()->GetName().c_str(),
  311. DebugStateTransit::StateTransitionString(m_effectorState, m_effectorTargetState).c_str());
  312. return false;
  313. }
  314. return true;
  315. }
  316. void KrakenEffectorComponent::BeginTransitionIfAcceptable(EffectorState targetState)
  317. {
  318. if (IsTransitionAcceptable(targetState))
  319. {
  320. m_currentStateTransitionTime = 0.0f;
  321. m_effectorTargetState = targetState;
  322. }
  323. }
  324. void KrakenEffectorComponent::InitializeStateProperties()
  325. {
  326. m_stateProperties.m_stateActions = {
  327. { EffectorState::IDLE,
  328. [this]()
  329. {
  330. LockManipulator(true);
  331. } },
  332. { EffectorState::PREPARED,
  333. []()
  334. {
  335. } },
  336. { EffectorState::PICKING,
  337. [this]()
  338. {
  339. if (m_currentStateTransitionTime > m_maxPickingTime)
  340. {
  341. AZ_Printf("m_onTriggerHandleBeginHandler", "%s : Failed to retrieve apple--------------------\n", GetEntity()->GetName().c_str());
  342. ApplePickingNotificationBus::Event(this->GetEntityId(),&ApplePickingNotifications::PickingFailed, "Timeout");
  343. }
  344. } },
  345. { EffectorState::PICKING_STABILIZE,
  346. [this]()
  347. {
  348. if (m_currentStateTransitionTime > m_stabilize_time)
  349. {
  350. BeginTransitionIfAcceptable(EffectorState::RETRIEVING_NOSE);
  351. }
  352. } },
  353. { EffectorState::RETRIEVING_NOSE,
  354. [this]()
  355. {
  356. if (m_currentStateTransitionTime > m_retrieve_nose_time)
  357. {
  358. BeginTransitionIfAcceptable(EffectorState::RETRIEVING);
  359. }
  360. } },
  361. {EffectorState::RETRIEVING,
  362. [this]() {
  363. // Continue if manipulator retraction was blocked
  364. if (m_currentStateTransitionTime > m_maxRetrieveTime) {
  365. BeginTransitionIfAcceptable(EffectorState::RETRIEVING_STABILIZE);
  366. }
  367. } },
  368. { EffectorState::RETRIEVING_STABILIZE,
  369. [this]()
  370. {
  371. if (m_currentStateTransitionTime > m_stabilize_time)
  372. {
  373. BeginTransitionIfAcceptable(EffectorState::PREPARED);
  374. }
  375. } }
  376. };
  377. m_stateProperties.m_allowedTransitions = {
  378. {
  379. { EffectorState::IDLE, EffectorState::PREPARED },
  380. [this]()
  381. {
  382. LockManipulator(false);
  383. ApplePickingNotificationBus::Event(GetEntityId(),&ApplePickingNotifications::EffectorReadyForPicking);
  384. },
  385. },
  386. {
  387. { EffectorState::PREPARED, EffectorState::PICKING },
  388. []()
  389. {
  390. },
  391. },
  392. {
  393. { EffectorState::PICKING, EffectorState::PICKING_STABILIZE },
  394. []()
  395. {
  396. },
  397. },
  398. {
  399. { EffectorState::PICKING_STABILIZE, EffectorState::RETRIEVING_NOSE },
  400. [this]()
  401. {
  402. if (!m_currentTask.IsValid())
  403. {
  404. AZ_Error("KrakenEffectorComponent", true, "%s : No valid task for current picking!",GetEntity()->GetName().c_str());
  405. return;
  406. }
  407. ManipulatorRequestBus::Event(m_manipulatorEntity, &ManipulatorRequest::RetrieveNose);
  408. },
  409. },
  410. {
  411. { EffectorState::RETRIEVING_NOSE, EffectorState::RETRIEVING },
  412. [this]()
  413. {
  414. ManipulatorRequestBus::Event(m_manipulatorEntity, &ManipulatorRequest::Retrieve);
  415. },
  416. },
  417. { // on skip
  418. { EffectorState::RETRIEVING_NOSE, EffectorState::RETRIEVING_STABILIZE },
  419. [this]()
  420. {
  421. ManipulatorRequestBus::Event(m_manipulatorEntity, &ManipulatorRequest::Retrieve);
  422. },
  423. },
  424. {
  425. { EffectorState::RETRIEVING, EffectorState::RETRIEVING_STABILIZE },
  426. []()
  427. {
  428. },
  429. },
  430. {
  431. { EffectorState::RETRIEVING_STABILIZE, EffectorState::PREPARED },
  432. [this]()
  433. {
  434. ApplePickingNotificationBus::Event(GetEntityId(),&ApplePickingNotifications::AppleRetrieved);
  435. },
  436. },
  437. {
  438. { EffectorState::PREPARED, EffectorState::IDLE },
  439. []()
  440. {
  441. },
  442. },
  443. {
  444. { EffectorState::PICKING, EffectorState::IDLE },
  445. [this]()
  446. {
  447. // apple picking was finished with timeout
  448. ManipulatorRequestBus::Event(m_manipulatorEntity, &ManipulatorRequest::Retrieve);
  449. },
  450. },
  451. };
  452. }
  453. const AZStd::function<void()>& KrakenEffectorComponent::GetCurrentStateAction() const
  454. {
  455. AZ_Assert(m_effectorState != EffectorState::INVALID, "%s : Effector is in an invalid state! Unable to access state properties.",GetEntity()->GetName().c_str());
  456. return m_stateProperties.m_stateActions.at(m_effectorState);
  457. }
  458. const AZStd::function<void()>& KrakenEffectorComponent::GetCurrentTransitionAction() const
  459. {
  460. return m_stateProperties.m_allowedTransitions.at(AZStd::make_pair(m_effectorState, m_effectorTargetState));
  461. }
  462. } // namespace AppleKraken