AttachmentComponent.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  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 "AttachmentComponent.h"
  9. #include <Atom/RPI.Reflect/Model/ModelAsset.h>
  10. #include <AzCore/Serialization/SerializeContext.h>
  11. #include <AzCore/RTTI/BehaviorContext.h>
  12. #include <AzCore/Component/Entity.h>
  13. #include <LmbrCentral/Animation/AttachmentComponentBus.h>
  14. #include <LmbrCentral/Animation/SkeletalHierarchyRequestBus.h>
  15. namespace AZ
  16. {
  17. namespace Render
  18. {
  19. /// Behavior Context handler for AttachmentComponentNotificationBus
  20. class BehaviorAttachmentComponentNotificationBusHandler : public LmbrCentral::AttachmentComponentNotificationBus::Handler,
  21. public AZ::BehaviorEBusHandler
  22. {
  23. public:
  24. AZ_EBUS_BEHAVIOR_BINDER(
  25. BehaviorAttachmentComponentNotificationBusHandler, "{636B95A0-5C7D-4EE7-8645-955665315451}", AZ::SystemAllocator,
  26. OnAttached, OnDetached);
  27. void OnAttached(AZ::EntityId id) override
  28. {
  29. Call(FN_OnAttached, id);
  30. }
  31. void OnDetached(AZ::EntityId id) override
  32. {
  33. Call(FN_OnDetached, id);
  34. }
  35. };
  36. void AttachmentConfiguration::Reflect(AZ::ReflectContext* context)
  37. {
  38. AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
  39. if (serializeContext)
  40. {
  41. serializeContext->Class<AttachmentConfiguration>()
  42. ->Version(1)
  43. ->Field("Target ID", &AttachmentConfiguration::m_targetId)
  44. ->Field("Target Bone Name", &AttachmentConfiguration::m_targetBoneName)
  45. ->Field("Target Offset", &AttachmentConfiguration::m_targetOffset)
  46. ->Field("Attached Initially", &AttachmentConfiguration::m_attachedInitially)
  47. ->Field("Scale Source", &AttachmentConfiguration::m_scaleSource);
  48. }
  49. AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context);
  50. if (behaviorContext)
  51. {
  52. behaviorContext->EBus<LmbrCentral::AttachmentComponentRequestBus>("AttachmentComponentRequestBus")
  53. ->Event("Attach", &LmbrCentral::AttachmentComponentRequestBus::Events::Attach)
  54. ->Event("Detach", &LmbrCentral::AttachmentComponentRequestBus::Events::Detach)
  55. ->Event("SetAttachmentOffset", &LmbrCentral::AttachmentComponentRequestBus::Events::SetAttachmentOffset)
  56. ->Event("GetJointName", &LmbrCentral::AttachmentComponentRequestBus::Events::GetJointName)
  57. ->Event("GetTargetEntityId", &LmbrCentral::AttachmentComponentRequestBus::Events::GetTargetEntityId)
  58. ->Event("GetOffset", &LmbrCentral::AttachmentComponentRequestBus::Events::GetOffset);
  59. behaviorContext->EBus<LmbrCentral::AttachmentComponentNotificationBus>("AttachmentComponentNotificationBus")
  60. ->Handler<BehaviorAttachmentComponentNotificationBusHandler>();
  61. }
  62. }
  63. void AttachmentComponent::Reflect(AZ::ReflectContext* context)
  64. {
  65. AttachmentConfiguration::Reflect(context);
  66. AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
  67. if (serializeContext)
  68. {
  69. serializeContext->Class<AttachmentComponent, AZ::Component>()->Version(1)->Field(
  70. "Configuration", &AttachmentComponent::m_initialConfiguration);
  71. }
  72. }
  73. //=========================================================================
  74. // BoneFollower
  75. //=========================================================================
  76. void BoneFollower::Activate(AZ::Entity* owner, const AttachmentConfiguration& configuration, bool targetCanAnimate)
  77. {
  78. AZ_Assert(owner, "owner is required");
  79. AZ_Assert(!m_ownerId.IsValid(), "BoneFollower is already Activated");
  80. m_ownerId = owner->GetId();
  81. m_targetCanAnimate = targetCanAnimate;
  82. m_isUpdatingOwnerTransform = false;
  83. m_scaleSource = configuration.m_scaleSource;
  84. m_cachedOwnerTransform = AZ::Transform::CreateIdentity();
  85. AZ::TransformBus::EventResult(m_cachedOwnerTransform, m_ownerId, &AZ::TransformBus::Events::GetWorldTM);
  86. if (configuration.m_attachedInitially)
  87. {
  88. Attach(configuration.m_targetId, configuration.m_targetBoneName.c_str(), configuration.m_targetOffset);
  89. }
  90. LmbrCentral::AttachmentComponentRequestBus::Handler::BusConnect(m_ownerId);
  91. }
  92. void BoneFollower::Deactivate()
  93. {
  94. AZ_Assert(m_ownerId.IsValid(), "BoneFollower was never Activated");
  95. LmbrCentral::AttachmentComponentRequestBus::Handler::BusDisconnect();
  96. Detach();
  97. m_ownerId.SetInvalid();
  98. }
  99. AZ::EntityId BoneFollower::GetTargetEntityId()
  100. {
  101. return m_targetId;
  102. }
  103. AZ::Transform BoneFollower::GetOffset()
  104. {
  105. return m_targetOffset;
  106. }
  107. void BoneFollower::Attach(AZ::EntityId targetId, const char* targetBoneName, const AZ::Transform& offset)
  108. {
  109. AZ_Assert(m_ownerId.IsValid(), "BoneFollower must be Activated to use.")
  110. // safe to try and detach, even if we weren't attached
  111. Detach();
  112. if (!targetId.IsValid())
  113. {
  114. return;
  115. }
  116. if (targetId == m_ownerId)
  117. {
  118. AZ_Error("Attachment Component", false, "AttachmentComponent cannot target itself");
  119. return;
  120. }
  121. // Note: the target entity may not be activated yet. That's ok.
  122. // When mesh is ready we are notified via MeshComponentEvents::OnModelReady
  123. // When transform is ready we are notified via TransformNotificationBus::OnTransformChanged
  124. m_targetId = targetId;
  125. m_targetBoneName = targetBoneName;
  126. m_targetOffset = offset;
  127. BindTargetBone();
  128. m_targetBoneTransform = AZ::Transform::Identity();
  129. m_isTargetEntityTransformKnown = false; // target's transform may not be available yet
  130. AZ::TransformBus::EventResult(
  131. m_cachedOwnerTransform, m_ownerId, &AZ::TransformBus::Events::GetWorldTM); // owner query will always succeed
  132. MeshComponentNotificationBus::Handler::BusConnect(m_targetId); // fires OnModelReady if asset is already ready
  133. AZ::TransformNotificationBus::Handler::BusConnect(m_targetId);
  134. if (m_targetCanAnimate)
  135. {
  136. // Only register for per-frame updates when target can animate
  137. AZ::TickBus::Handler::BusConnect();
  138. }
  139. // update owner's transform
  140. UpdateOwnerTransformIfNecessary();
  141. // alert others that we've attached
  142. LmbrCentral::AttachmentComponentNotificationBus::Event(m_targetId, &LmbrCentral::AttachmentComponentNotificationBus::Events::OnAttached, m_ownerId);
  143. }
  144. void BoneFollower::Detach()
  145. {
  146. AZ_Assert(m_ownerId.IsValid(), "BoneFollower must be Activated to use.");
  147. if (m_targetId.IsValid())
  148. {
  149. // alert others that we're detaching
  150. LmbrCentral::AttachmentComponentNotificationBus::Event(
  151. m_targetId, &LmbrCentral::AttachmentComponentNotificationBus::Events::OnDetached, m_ownerId);
  152. MeshComponentNotificationBus::Handler::BusDisconnect();
  153. AZ::TransformNotificationBus::Handler::BusDisconnect(m_targetId);
  154. AZ::TickBus::Handler::BusDisconnect();
  155. m_targetId.SetInvalid();
  156. }
  157. }
  158. const char* BoneFollower::GetJointName()
  159. {
  160. return m_targetBoneName.c_str();
  161. }
  162. void BoneFollower::SetAttachmentOffset(const AZ::Transform& offset)
  163. {
  164. AZ_Assert(m_ownerId.IsValid(), "BoneFollower must be Activated to use.");
  165. if (m_targetId.IsValid())
  166. {
  167. m_targetOffset = offset;
  168. UpdateOwnerTransformIfNecessary();
  169. }
  170. }
  171. void BoneFollower::OnModelReady([[maybe_unused]] const AZ::Data::Asset<AZ::RPI::ModelAsset>& modelAsset, [[maybe_unused]] const AZ::Data::Instance<AZ::RPI::Model>& model)
  172. {
  173. // reset character values
  174. BindTargetBone();
  175. m_targetBoneTransform = QueryBoneTransform();
  176. // move owner if necessary
  177. UpdateOwnerTransformIfNecessary();
  178. }
  179. void BoneFollower::BindTargetBone()
  180. {
  181. m_targetBoneId = -1;
  182. LmbrCentral::SkeletalHierarchyRequestBus::EventResult(
  183. m_targetBoneId, m_targetId, &LmbrCentral::SkeletalHierarchyRequests::GetJointIndexByName, m_targetBoneName.c_str());
  184. }
  185. void BoneFollower::UpdateOwnerTransformIfNecessary()
  186. {
  187. // Can't update until target entity's transform is known
  188. if (!m_isTargetEntityTransformKnown)
  189. {
  190. if (AZ::TransformBus::GetNumOfEventHandlers(m_targetId) == 0)
  191. {
  192. return;
  193. }
  194. AZ::TransformBus::EventResult(m_targetEntityTransform, m_targetId, &AZ::TransformBus::Events::GetWorldTM);
  195. m_isTargetEntityTransformKnown = true;
  196. }
  197. AZ::Transform finalTransform;
  198. if (m_scaleSource == AttachmentConfiguration::ScaleSource::WorldScale)
  199. {
  200. // apply offset in world-space
  201. finalTransform = m_targetEntityTransform * m_targetBoneTransform;
  202. finalTransform.SetUniformScale(1.0f);
  203. finalTransform *= m_targetOffset;
  204. }
  205. else if (m_scaleSource == AttachmentConfiguration::ScaleSource::TargetEntityScale)
  206. {
  207. // apply offset in target-entity-space (ignoring bone scale)
  208. AZ::Transform boneNoScale = m_targetBoneTransform;
  209. boneNoScale.SetUniformScale(1.0f);
  210. finalTransform = m_targetEntityTransform * boneNoScale * m_targetOffset;
  211. }
  212. else // AttachmentConfiguration::ScaleSource::TargetEntityScale
  213. {
  214. // apply offset in target-bone-space
  215. finalTransform = m_targetEntityTransform * m_targetBoneTransform * m_targetOffset;
  216. }
  217. if (m_cachedOwnerTransform != finalTransform)
  218. {
  219. AZ_Warning(
  220. "Attachment Component", !m_isUpdatingOwnerTransform,
  221. "AttachmentComponent detected a cycle when updating transform, do not target child entities.");
  222. if (!m_isUpdatingOwnerTransform)
  223. {
  224. m_cachedOwnerTransform = finalTransform;
  225. m_isUpdatingOwnerTransform = true;
  226. AZ::TransformBus::Event(m_ownerId, &AZ::TransformBus::Events::SetWorldTM, finalTransform);
  227. m_isUpdatingOwnerTransform = false;
  228. }
  229. }
  230. }
  231. AZ::Transform BoneFollower::QueryBoneTransform() const
  232. {
  233. AZ::Transform boneTransform = AZ::Transform::CreateIdentity();
  234. if (m_targetBoneId >= 0)
  235. {
  236. LmbrCentral::SkeletalHierarchyRequestBus::EventResult(
  237. boneTransform, m_targetId, &LmbrCentral::SkeletalHierarchyRequests::GetJointTransformCharacterRelative, m_targetBoneId);
  238. }
  239. return boneTransform;
  240. }
  241. // fires when target's transform changes
  242. void BoneFollower::OnTransformChanged(const AZ::Transform& /*local*/, const AZ::Transform& world)
  243. {
  244. m_targetEntityTransform = world;
  245. m_isTargetEntityTransformKnown = true;
  246. UpdateOwnerTransformIfNecessary();
  247. }
  248. void BoneFollower::OnTick(float /*deltaTime*/, AZ::ScriptTimePoint /*time*/)
  249. {
  250. m_targetBoneTransform = QueryBoneTransform();
  251. UpdateOwnerTransformIfNecessary();
  252. }
  253. int BoneFollower::GetTickOrder()
  254. {
  255. return AZ::TICK_ATTACHMENT;
  256. }
  257. void BoneFollower::Reattach(bool detachFirst)
  258. {
  259. #ifdef AZ_ENABLE_TRACING
  260. AZ::Entity* ownerEntity = nullptr;
  261. AZ::Entity* targetEntity = nullptr;
  262. AZ::ComponentApplicationBus::BroadcastResult(ownerEntity, &AZ::ComponentApplicationBus::Events::FindEntity, m_ownerId);
  263. AZ::ComponentApplicationBus::BroadcastResult(targetEntity, &AZ::ComponentApplicationBus::Events::FindEntity, m_targetId);
  264. AZ_TracePrintf(
  265. "BoneFollower", "Reattaching entity '%s' to entity '%s'", ownerEntity ? ownerEntity->GetName().c_str() : "",
  266. targetEntity ? targetEntity->GetName().c_str() : "");
  267. #endif
  268. if (m_targetId.IsValid() && detachFirst)
  269. {
  270. LmbrCentral::AttachmentComponentNotificationBus::Event(m_targetId, &LmbrCentral::AttachmentComponentNotificationBus::Events::OnDetached, m_ownerId);
  271. }
  272. if (m_targetId != m_ownerId)
  273. {
  274. LmbrCentral::AttachmentComponentNotificationBus::Event(m_targetId, &LmbrCentral::AttachmentComponentNotificationBus::Events::OnAttached, m_ownerId);
  275. }
  276. }
  277. //=========================================================================
  278. // AttachmentComponent
  279. //=========================================================================
  280. void AttachmentComponent::Activate()
  281. {
  282. #ifdef AZ_ENABLE_TRACING
  283. bool isStaticTransform = false;
  284. AZ::TransformBus::EventResult(isStaticTransform, GetEntityId(), &AZ::TransformBus::Events::IsStaticTransform);
  285. AZ_Warning(
  286. "Attachment Component", !isStaticTransform, "Attachment needs to move, but entity '%s' %s has a static transform.",
  287. GetEntity()->GetName().c_str(), GetEntityId().ToString().c_str());
  288. #endif
  289. m_boneFollower.Activate(GetEntity(), m_initialConfiguration, true);
  290. }
  291. void AttachmentComponent::Deactivate()
  292. {
  293. m_boneFollower.Deactivate();
  294. }
  295. } // namespace Render
  296. } // namespace AZ