NetworkHitVolumesComponent.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  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 <Multiplayer/Components/NetworkHitVolumesComponent.h>
  9. #include <AzFramework/Components/TransformComponent.h>
  10. #include <AzFramework/Physics/Common/PhysicsTypes.h>
  11. #include <AzFramework/Physics/CharacterBus.h>
  12. #include <AzFramework/Physics/Character.h>
  13. #include <AzFramework/Physics/SystemBus.h>
  14. #include <MCore/Source/AzCoreConversions.h>
  15. #include <Integration/ActorComponentBus.h>
  16. namespace Multiplayer
  17. {
  18. AZ_CVAR(bool, bg_DrawArticulatedHitVolumes, false, nullptr, AZ::ConsoleFunctorFlags::Null, "Enables debug draw of articulated hit volumes");
  19. AZ_CVAR(float, bg_DrawDebugHitVolumeLifetime, 0.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "The lifetime for hit volume draw-debug shapes");
  20. AZ_CVAR(float, bg_RewindPositionTolerance, 0.0001f, nullptr, AZ::ConsoleFunctorFlags::Null, "Don't sync the physx entity if the square of delta position is less than this value");
  21. AZ_CVAR(float, bg_RewindOrientationTolerance, 0.001f, nullptr, AZ::ConsoleFunctorFlags::Null, "Don't sync the physx entity if the square of delta orientation is less than this value");
  22. NetworkHitVolumesComponent::AnimatedHitVolume::AnimatedHitVolume
  23. (
  24. AzNetworking::ConnectionId connectionId,
  25. Physics::CharacterRequests* character,
  26. const char* hitVolumeName,
  27. const Physics::ColliderConfiguration* colliderConfig,
  28. const Physics::ShapeConfiguration* shapeConfig,
  29. const uint32_t jointIndex
  30. )
  31. : m_colliderConfig(colliderConfig)
  32. , m_shapeConfig(shapeConfig)
  33. , m_jointIndex(jointIndex)
  34. {
  35. m_transform.SetOwningConnectionId(connectionId);
  36. m_colliderOffSetTransform = AZ::Transform::CreateFromQuaternionAndTranslation(m_colliderConfig->m_rotation, m_colliderConfig->m_position);
  37. if (m_colliderConfig->m_isExclusive)
  38. {
  39. Physics::SystemRequestBus::BroadcastResult(m_physicsShape, &Physics::SystemRequests::CreateShape, *m_colliderConfig, *m_shapeConfig);
  40. }
  41. else
  42. {
  43. Physics::ColliderConfiguration colliderConfiguration = *m_colliderConfig;
  44. colliderConfiguration.m_isExclusive = true;
  45. colliderConfiguration.m_isSimulated = false;
  46. colliderConfiguration.m_isInSceneQueries = true;
  47. Physics::SystemRequestBus::BroadcastResult(m_physicsShape, &Physics::SystemRequests::CreateShape, colliderConfiguration, *m_shapeConfig);
  48. }
  49. if (m_physicsShape)
  50. {
  51. m_physicsShape->SetName(hitVolumeName);
  52. character->GetCharacter()->AttachShape(m_physicsShape);
  53. }
  54. }
  55. void NetworkHitVolumesComponent::AnimatedHitVolume::UpdateTransform(const AZ::Transform& transform)
  56. {
  57. m_transform = transform;
  58. m_physicsShape->SetLocalPose(transform.GetTranslation(), transform.GetRotation());
  59. }
  60. void NetworkHitVolumesComponent::AnimatedHitVolume::SyncToCurrentTransform()
  61. {
  62. AZ::Transform rewoundTransform;
  63. const AZ::Transform& targetTransform = m_transform.Get();
  64. const float blendFactor = Multiplayer::GetNetworkTime()->GetHostBlendFactor();
  65. if (blendFactor < 1.f)
  66. {
  67. // If a blend factor was supplied, interpolate the transform appropriately
  68. const AZ::Transform& previousTransform = m_transform.GetPrevious();
  69. rewoundTransform.SetRotation(previousTransform.GetRotation().Slerp(targetTransform.GetRotation(), blendFactor));
  70. rewoundTransform.SetTranslation(previousTransform.GetTranslation().Lerp(targetTransform.GetTranslation(), blendFactor));
  71. rewoundTransform.SetUniformScale(AZ::Lerp(previousTransform.GetUniformScale(), targetTransform.GetUniformScale(), blendFactor));
  72. }
  73. else
  74. {
  75. rewoundTransform = m_transform.Get();
  76. }
  77. const AZ::Transform physicsTransform = AZ::Transform::CreateFromQuaternionAndTranslation(m_physicsShape->GetLocalPose().second, m_physicsShape->GetLocalPose().first);
  78. // Don't call SetLocalPose unless the transforms are actually different
  79. const AZ::Vector3 positionDelta = physicsTransform.GetTranslation() - rewoundTransform.GetTranslation();
  80. const AZ::Quaternion orientationDelta = physicsTransform.GetRotation() - rewoundTransform.GetRotation();
  81. if ((positionDelta.GetLengthSq() >= bg_RewindPositionTolerance) || (orientationDelta.GetLengthSq() >= bg_RewindOrientationTolerance))
  82. {
  83. m_physicsShape->SetLocalPose(rewoundTransform.GetTranslation(), rewoundTransform.GetRotation());
  84. }
  85. }
  86. void NetworkHitVolumesComponent::NetworkHitVolumesComponent::Reflect(AZ::ReflectContext* context)
  87. {
  88. AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
  89. if (serializeContext)
  90. {
  91. serializeContext->Class<NetworkHitVolumesComponent, NetworkHitVolumesComponentBase>()
  92. ->Version(1);
  93. }
  94. NetworkHitVolumesComponentBase::Reflect(context);
  95. }
  96. NetworkHitVolumesComponent::NetworkHitVolumesComponent()
  97. : m_syncRewindHandler([this]() { OnSyncRewind(); })
  98. , m_preRenderHandler([this](float deltaTime) { OnPreRender(deltaTime); })
  99. , m_transformChangedHandler([this](const AZ::Transform&, const AZ::Transform& worldTm) { OnTransformUpdate(worldTm); })
  100. {
  101. ;
  102. }
  103. void NetworkHitVolumesComponent::OnInit()
  104. {
  105. ;
  106. }
  107. void NetworkHitVolumesComponent::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
  108. {
  109. EMotionFX::Integration::ActorComponentNotificationBus::Handler::BusConnect(GetEntityId());
  110. GetNetBindComponent()->AddEntitySyncRewindEventHandler(m_syncRewindHandler);
  111. GetNetBindComponent()->AddEntityPreRenderEventHandler(m_preRenderHandler);
  112. GetTransformComponent()->BindTransformChangedEventHandler(m_transformChangedHandler);
  113. OnTransformUpdate(GetTransformComponent()->GetWorldTM());
  114. // During activation the character controller is not created yet.
  115. // Connect to CharacterNotificationBus to listen when it's activated after creation.
  116. Physics::CharacterNotificationBus::Handler::BusConnect(GetEntityId());
  117. }
  118. void NetworkHitVolumesComponent::OnCharacterActivated([[maybe_unused]] const AZ::EntityId& entityId)
  119. {
  120. m_physicsCharacter = Physics::CharacterRequestBus::FindFirstHandler(GetEntityId());
  121. }
  122. void NetworkHitVolumesComponent::OnCharacterDeactivated([[maybe_unused]] const AZ::EntityId& entityId)
  123. {
  124. DestroyHitVolumes();
  125. m_physicsCharacter = nullptr;
  126. }
  127. void NetworkHitVolumesComponent::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
  128. {
  129. m_debugDisplay = nullptr;
  130. m_syncRewindHandler.Disconnect();
  131. m_preRenderHandler.Disconnect();
  132. m_transformChangedHandler.Disconnect();
  133. DestroyHitVolumes();
  134. Physics::CharacterNotificationBus::Handler::BusDisconnect();
  135. EMotionFX::Integration::ActorComponentNotificationBus::Handler::BusDisconnect();
  136. }
  137. void NetworkHitVolumesComponent::OnPreRender([[maybe_unused]] float deltaTime)
  138. {
  139. if (m_animatedHitVolumes.empty())
  140. {
  141. CreateHitVolumes();
  142. }
  143. AZ::Vector3 position, scale;
  144. AZ::Quaternion rotation;
  145. for (AnimatedHitVolume& hitVolume : m_animatedHitVolumes)
  146. {
  147. m_actorComponent->GetJointTransformComponents(hitVolume.m_jointIndex, EMotionFX::Integration::Space::ModelSpace, position, rotation, scale);
  148. hitVolume.UpdateTransform(AZ::Transform::CreateFromQuaternionAndTranslation(rotation, position) * hitVolume.m_colliderOffSetTransform);
  149. }
  150. if (bg_DrawArticulatedHitVolumes)
  151. {
  152. DrawDebugHitVolumes();
  153. }
  154. }
  155. void NetworkHitVolumesComponent::OnTransformUpdate([[maybe_unused]] const AZ::Transform& transform)
  156. {
  157. OnSyncRewind();
  158. }
  159. void NetworkHitVolumesComponent::OnSyncRewind()
  160. {
  161. if (m_physicsCharacter && m_physicsCharacter->GetCharacter())
  162. {
  163. uint32_t frameId = static_cast<uint32_t>(Multiplayer::GetNetworkTime()->GetHostFrameId());
  164. m_physicsCharacter->GetCharacter()->SetFrameId(frameId);
  165. }
  166. for (AnimatedHitVolume& hitVolume : m_animatedHitVolumes)
  167. {
  168. hitVolume.SyncToCurrentTransform();
  169. }
  170. }
  171. void NetworkHitVolumesComponent::CreateHitVolumes()
  172. {
  173. if (m_physicsCharacter == nullptr || m_actorComponent == nullptr)
  174. {
  175. return;
  176. }
  177. const Physics::AnimationConfiguration* physicsConfig = m_actorComponent->GetPhysicsConfig();
  178. if (physicsConfig == nullptr)
  179. {
  180. return;
  181. }
  182. m_hitDetectionConfig = &physicsConfig->m_hitDetectionConfig;
  183. const AzNetworking::ConnectionId owningConnectionId = GetNetBindComponent()->GetOwningConnectionId();
  184. m_animatedHitVolumes.reserve(m_hitDetectionConfig->m_nodes.size());
  185. for (const Physics::CharacterColliderNodeConfiguration& nodeConfig : m_hitDetectionConfig->m_nodes)
  186. {
  187. const AZStd::size_t jointIndex = m_actorComponent->GetJointIndexByName(nodeConfig.m_name.c_str());
  188. if (jointIndex == EMotionFX::Integration::ActorComponentRequests::s_invalidJointIndex)
  189. {
  190. continue;
  191. }
  192. for (const AzPhysics::ShapeColliderPair& coliderPair : nodeConfig.m_shapes)
  193. {
  194. const Physics::ColliderConfiguration* colliderConfig = coliderPair.first.get();
  195. Physics::ShapeConfiguration* shapeConfig = coliderPair.second.get();
  196. m_animatedHitVolumes.emplace_back(owningConnectionId, m_physicsCharacter, nodeConfig.m_name.c_str(), colliderConfig, shapeConfig, aznumeric_cast<uint32_t>(jointIndex));
  197. }
  198. }
  199. }
  200. void NetworkHitVolumesComponent::DestroyHitVolumes()
  201. {
  202. m_animatedHitVolumes.clear();
  203. }
  204. void NetworkHitVolumesComponent::OnActorInstanceCreated([[maybe_unused]] EMotionFX::ActorInstance* actorInstance)
  205. {
  206. m_actorComponent = EMotionFX::Integration::ActorComponentRequestBus::FindFirstHandler(GetEntity()->GetId());
  207. }
  208. void NetworkHitVolumesComponent::OnActorInstanceDestroyed([[maybe_unused]] EMotionFX::ActorInstance* actorInstance)
  209. {
  210. DestroyHitVolumes();
  211. m_actorComponent = nullptr;
  212. }
  213. void NetworkHitVolumesComponent::DrawDebugHitVolumes()
  214. {
  215. if (m_debugDisplay == nullptr)
  216. {
  217. AzFramework::DebugDisplayRequestBus::BusPtr debugDisplayBus;
  218. AzFramework::DebugDisplayRequestBus::Bind(debugDisplayBus, AzFramework::g_defaultSceneEntityDebugDisplayId);
  219. m_debugDisplay = AzFramework::DebugDisplayRequestBus::FindFirstHandler(debugDisplayBus);
  220. }
  221. if (m_debugDisplay != nullptr)
  222. {
  223. const AZ::u32 previousState = m_debugDisplay->GetState();
  224. m_debugDisplay->SetColor(AZ::Colors::Blue);
  225. AZ::Transform rigidBodyTransform = GetEntity()->GetTransform()->GetWorldTM();
  226. for (const AnimatedHitVolume& hitVolume : m_animatedHitVolumes)
  227. {
  228. AZ::Vector3 jointPosition = AZ::Vector3::CreateZero();
  229. AZ::Vector3 jointScale = AZ::Vector3::CreateOne();
  230. AZ::Quaternion jointRotation = AZ::Quaternion::CreateIdentity();
  231. m_actorComponent->GetJointTransformComponents(
  232. hitVolume.m_jointIndex, EMotionFX::Integration::Space::ModelSpace, jointPosition, jointRotation, jointScale);
  233. AZ::Transform colliderTransformNoScale = rigidBodyTransform *
  234. AZ::Transform::CreateFromQuaternionAndTranslation(jointRotation, jointPosition) * hitVolume.m_colliderOffSetTransform;
  235. m_debugDisplay->PushMatrix(colliderTransformNoScale);
  236. if (const Physics::SphereShapeConfiguration* sphereCollider =
  237. azrtti_cast<const Physics::SphereShapeConfiguration*>(hitVolume.m_shapeConfig))
  238. {
  239. m_debugDisplay->DrawWireSphere(AZ::Vector3::CreateZero(), sphereCollider->m_radius);
  240. }
  241. else if (const Physics::CapsuleShapeConfiguration* capsuleCollider =
  242. azrtti_cast<const Physics::CapsuleShapeConfiguration*>(hitVolume.m_shapeConfig))
  243. {
  244. const float radius = capsuleCollider->m_radius;
  245. const float height = capsuleCollider->m_height;
  246. m_debugDisplay->DrawWireCapsule(AZ::Vector3::CreateZero(), AZ::Vector3::CreateAxisZ(), radius, height * 0.5f);
  247. }
  248. else if (const Physics::BoxShapeConfiguration* boxCollider =
  249. azrtti_cast<const Physics::BoxShapeConfiguration*>(hitVolume.m_shapeConfig))
  250. {
  251. AZ::Vector3 dimensions = boxCollider->m_dimensions;
  252. m_debugDisplay->DrawWireBox(dimensions * -0.5f, dimensions * 0.5f);
  253. }
  254. m_debugDisplay->PopMatrix();
  255. }
  256. m_debugDisplay->SetState(previousState);
  257. }
  258. }
  259. } // namespace Multiplayer