merging latest development

Signed-off-by: kberg-amzn <karlberg@amazon.com>
This commit is contained in:
kberg-amzn
2021-09-20 14:07:49 -07:00
12 changed files with 872 additions and 1 deletions
+3
View File
@@ -25,6 +25,9 @@ ly_add_target(
AZ::AzCore
AZ::AzFramework
AZ::AzNetworking
PRIVATE
Gem::EMotionFXStaticLib
Gem::PhysX.Static
AUTOGEN_RULES
*.AutoPackets.xml,AutoPackets_Header.jinja,$path/$fileprefix.AutoPackets.h
*.AutoPackets.xml,AutoPackets_Inline.jinja,$path/$fileprefix.AutoPackets.inl
@@ -0,0 +1,82 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <Source/AutoGen/NetworkCharacterComponent.AutoComponent.h>
#include <PhysX/CharacterGameplayBus.h>
#include <Multiplayer/Components/NetBindComponent.h>
namespace Physics
{
class Character;
}
namespace Multiplayer
{
//! NetworkCharacterComponent
//! Provides multiplayer support for game-play player characters.
class NetworkCharacterComponent
: public NetworkCharacterComponentBase
, private PhysX::CharacterGameplayRequestBus::Handler
{
friend class NetworkCharacterComponentController;
public:
AZ_MULTIPLAYER_COMPONENT(Multiplayer::NetworkCharacterComponent, s_networkCharacterComponentConcreteUuid, Multiplayer::NetworkCharacterComponentBase)
static void Reflect(AZ::ReflectContext* context);
NetworkCharacterComponent();
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC_CE("NetworkRigidBodyService"));
}
// AZ::Component
void OnInit() override {}
void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
private:
void OnTranslationChangedEvent(const AZ::Vector3& translation);
void OnSyncRewind();
// CharacterGameplayRequestBus
bool IsOnGround() const override;
float GetGravityMultiplier() const override { return {}; }
void SetGravityMultiplier([[maybe_unused]] float gravityMultiplier) override {}
AZ::Vector3 GetFallingVelocity() const override { return {}; }
void SetFallingVelocity([[maybe_unused]] const AZ::Vector3& fallingVelocity) override {}
Physics::Character* m_physicsCharacter = nullptr;
Multiplayer::EntitySyncRewindEvent::Handler m_syncRewindHandler = Multiplayer::EntitySyncRewindEvent::Handler([this]() { OnSyncRewind(); });
AZ::Event<AZ::Vector3>::Handler m_translationEventHandler;
};
//! NetworkCharacterComponentController
//! This is the network controller for NetworkCharacterComponent.
//! Class provides the ability to move characters in physical space while keeping the network in-sync.
class NetworkCharacterComponentController
: public NetworkCharacterComponentControllerBase
{
public:
NetworkCharacterComponentController(NetworkCharacterComponent& parent);
// NetworkCharacterComponentControllerBase
void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
//! TryMoveWithVelocity
//! Will move this character entity kinematically through physical world while also ensuring the network stays in-sync.
//! Velocity will be applied over delta-time to determine the movement amount.
//! Returns this entity's world-space position after the move.
AZ::Vector3 TryMoveWithVelocity(const AZ::Vector3& velocity, float deltaTime);
};
}
@@ -0,0 +1,90 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <Source/AutoGen/NetworkHitVolumesComponent.AutoComponent.h>
#include <Multiplayer/Components/NetBindComponent.h>
#include <Integration/ActorComponentBus.h>
#include <AzCore/Component/TransformBus.h>
namespace Physics
{
class CharacterRequests;
class CharacterHitDetectionConfiguration;
}
namespace Multiplayer
{
class NetworkHitVolumesComponent
: public NetworkHitVolumesComponentBase
, private EMotionFX::Integration::ActorComponentNotificationBus::Handler
{
public:
struct AnimatedHitVolume final
{
AnimatedHitVolume
(
AzNetworking::ConnectionId connectionId,
Physics::CharacterRequests* character,
const char* hitVolumeName,
const Physics::ColliderConfiguration* colliderConfig,
const Physics::ShapeConfiguration* shapeConfig,
const uint32_t jointIndex
);
~AnimatedHitVolume() = default;
void UpdateTransform(const AZ::Transform& transform);
void SyncToCurrentTransform();
Multiplayer::RewindableObject<AZ::Transform, Multiplayer::RewindHistorySize> m_transform;
AZStd::shared_ptr<Physics::Shape> m_physicsShape;
// Cached so we don't have to do subsequent lookups by name
const Physics::ColliderConfiguration* m_colliderConfig = nullptr;
const Physics::ShapeConfiguration* m_shapeConfig = nullptr;
AZ::Transform m_colliderOffSetTransform;
const AZ::u32 m_jointIndex = 0;
};
AZ_MULTIPLAYER_COMPONENT(Multiplayer::NetworkHitVolumesComponent, s_networkHitVolumesComponentConcreteUuid, Multiplayer::NetworkHitVolumesComponentBase);
static void Reflect(AZ::ReflectContext* context);
NetworkHitVolumesComponent();
void OnInit() override;
void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
private:
void OnPreRender(float deltaTime, float blendFactor);
void OnTransformUpdate(const AZ::Transform& transform);
void OnSyncRewind();
void CreateHitVolumes();
void DestroyHitVolumes();
//! ActorComponentNotificationBus::Handler
//! @{
void OnActorInstanceCreated(EMotionFX::ActorInstance* actorInstance) override;
void OnActorInstanceDestroyed(EMotionFX::ActorInstance* actorInstance) override;
//! @}
Physics::CharacterRequests* m_physicsCharacter = nullptr;
EMotionFX::Integration::ActorComponentRequests* m_actorComponent = nullptr;
const Physics::CharacterColliderConfiguration* m_hitDetectionConfig = nullptr;
AZStd::vector<AnimatedHitVolume> m_animatedHitVolumes;
Multiplayer::EntitySyncRewindEvent::Handler m_syncRewindHandler;
Multiplayer::EntityPreRenderEvent::Handler m_preRenderHandler;
AZ::TransformChangedEvent::Handler m_transformChangedHandler;
};
}
@@ -0,0 +1,68 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <Source/AutoGen/NetworkRigidBodyComponent.AutoComponent.h>
#include <AzCore/Component/TransformBus.h>
#include <Multiplayer/Components/NetBindComponent.h>
namespace Physics
{
class RigidBodyRequests;
}
namespace Multiplayer
{
//! Bus for requests to the network rigid body component.
class NetworkRigidBodyRequests : public AZ::ComponentBus
{
};
using NetworkRigidBodyRequestBus = AZ::EBus<NetworkRigidBodyRequests>;
class NetworkRigidBodyComponent final
: public NetworkRigidBodyComponentBase
, private NetworkRigidBodyRequestBus::Handler
{
friend class NetworkRigidBodyComponentController;
public:
AZ_MULTIPLAYER_COMPONENT(
Multiplayer::NetworkRigidBodyComponent, s_networkRigidBodyComponentConcreteUuid, Multiplayer::NetworkRigidBodyComponentBase);
static void Reflect(AZ::ReflectContext* context);
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent);
NetworkRigidBodyComponent();
void OnInit() override;
void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
private:
void OnTransformUpdate(const AZ::Transform& worldTm);
void OnSyncRewind();
Multiplayer::EntitySyncRewindEvent::Handler m_syncRewindHandler;
AZ::TransformChangedEvent::Handler m_transformChangedHandler;
Physics::RigidBodyRequests* m_physicsRigidBodyComponent = nullptr;
Multiplayer::RewindableObject<AZ::Transform, Multiplayer::RewindHistorySize> m_transform;
};
class NetworkRigidBodyComponentController
: public NetworkRigidBodyComponentControllerBase
{
public:
NetworkRigidBodyComponentController(NetworkRigidBodyComponent& parent);
void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
void HandleSendApplyImpulse(AzNetworking::IConnection* invokingConnection, const AZ::Vector3& impulse, const AZ::Vector3& worldPoint) override;
};
} // namespace Multiplayer
@@ -14,7 +14,7 @@
{% set Namespace = dataFiles[0].attrib['Namespace'] %}
{% for Component in dataFiles %}
{% if Component.attrib['Namespace'] != Namespace %}
#error "mismatched component namespaces detected in declared multiplayer components, expected {{ Namespace }} but found {{ Component.attrib['Namespace'] }}"
#error "mismatched component namespaces detected in declared multiplayer components, expected {{ Namespace }} but {{ Component.attrib['Name'] }} is using {{ Component.attrib['Namespace'] }} namespace."
{% endif %}
{% endfor %}
namespace {{ Namespace }}
@@ -0,0 +1,12 @@
<?xml version="1.0"?>
<Component
Name="NetworkCharacterComponent"
Namespace="Multiplayer"
OverrideComponent="true"
OverrideController="true"
OverrideInclude="Multiplayer/Components/NetworkCharacterComponent.h"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ComponentRelation Constraint="Required" HasController="true" Name="NetworkTransformComponent" Namespace="Multiplayer" Include="Multiplayer/Components/NetworkTransformComponent.h" />
</Component>
@@ -0,0 +1,12 @@
<?xml version="1.0"?>
<Component
Name="NetworkHitVolumesComponent"
Namespace="Multiplayer"
OverrideComponent="true"
OverrideController="false"
OverrideInclude="Multiplayer/Components/NetworkHitVolumesComponent.h"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ComponentRelation Constraint="Weak" HasController="false" Name="TransformComponent" Namespace="AzFramework" Include="AzFramework/Components/TransformComponent.h" />
</Component>
@@ -0,0 +1,18 @@
<?xml version="1.0"?>
<Component
Name="NetworkRigidBodyComponent"
Namespace="Multiplayer"
OverrideComponent="true"
OverrideController="true"
OverrideInclude="Multiplayer/Components/NetworkRigidBodyComponent.h"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ComponentRelation Constraint="Required" HasController="true" Name="NetworkTransformComponent" Namespace="Multiplayer" Include="Multiplayer/Components/NetworkTransformComponent.h" />
<RemoteProcedure Name="SendApplyImpulse" InvokeFrom="Server" HandleOn="Authority" IsPublic="true" IsReliable="true" GenerateEventBindings="false" Description="Applies an impulse">
<Param Type="AZ::Vector3" Name="impulse" />
<Param Type="AZ::Vector3" Name="worldPoint" />
</RemoteProcedure>
</Component>
@@ -0,0 +1,209 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <Multiplayer/Components/NetworkCharacterComponent.h>
#include <Multiplayer/Components/NetworkRigidBodyComponent.h>
#include <AzFramework/Visibility/EntityBoundsUnionBus.h>
#include <AzFramework/Physics/CharacterBus.h>
#include <AzFramework/Physics/Character.h>
#include <Multiplayer/Components/NetworkTransformComponent.h>
#include <Multiplayer/NetworkTime/INetworkTime.h>
#include <PhysXCharacters/API/CharacterController.h>
#include <PhysX/PhysXLocks.h>
#include <PhysX/Utils.h>
namespace Multiplayer
{
bool CollisionLayerBasedControllerFilter(const physx::PxController& controllerA, const physx::PxController& controllerB)
{
PHYSX_SCENE_READ_LOCK(controllerA.getActor()->getScene());
physx::PxRigidDynamic* actorA = controllerA.getActor();
physx::PxRigidDynamic* actorB = controllerB.getActor();
if (actorA && actorA->getNbShapes() > 0 && actorB && actorB->getNbShapes() > 0)
{
physx::PxShape* shapeA = nullptr;
actorA->getShapes(&shapeA, 1, 0);
physx::PxFilterData filterDataA = shapeA->getSimulationFilterData();
physx::PxShape* shapeB = nullptr;
actorB->getShapes(&shapeB, 1, 0);
physx::PxFilterData filterDataB = shapeB->getSimulationFilterData();
return PhysX::Utils::Collision::ShouldCollide(filterDataA, filterDataB);
}
return true;
}
physx::PxQueryHitType::Enum CollisionLayerBasedObjectPreFilter(
const physx::PxFilterData& filterData,
const physx::PxShape* shape,
const physx::PxRigidActor* actor,
[[maybe_unused]] physx::PxHitFlags& queryFlags)
{
// non-kinematic dynamic bodies should not impede the movement of the character
if (actor->getConcreteType() == physx::PxConcreteType::eRIGID_DYNAMIC)
{
const physx::PxRigidDynamic* rigidDynamic = static_cast<const physx::PxRigidDynamic*>(actor);
bool isKinematic = (rigidDynamic->getRigidBodyFlags() & physx::PxRigidBodyFlag::eKINEMATIC);
if (isKinematic)
{
const PhysX::ActorData* actorData = PhysX::Utils::GetUserData(rigidDynamic);
if (actorData)
{
const AZ::EntityId entityId = actorData->GetEntityId();
if (Multiplayer::NetworkRigidBodyRequestBus::FindFirstHandler(entityId) != nullptr)
{
// Network rigid bodies are kinematic on the client but dynamic on the server,
// hence filtering treats these actors as dynamic to support client prediction and avoid desyncs
isKinematic = false;
}
}
}
if (!isKinematic)
{
return physx::PxQueryHitType::eNONE;
}
}
// all other cases should be determined by collision filters
if (PhysX::Utils::Collision::ShouldCollide(filterData, shape->getSimulationFilterData()))
{
return physx::PxQueryHitType::eBLOCK;
}
return physx::PxQueryHitType::eNONE;
}
void NetworkCharacterComponent::NetworkCharacterComponent::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<NetworkCharacterComponent, NetworkCharacterComponentBase>()
->Version(1);
}
NetworkCharacterComponentBase::Reflect(context);
}
NetworkCharacterComponent::NetworkCharacterComponent()
: m_translationEventHandler([this](const AZ::Vector3& translation) { OnTranslationChangedEvent(translation); })
{
}
void NetworkCharacterComponent::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
{
Physics::CharacterRequests* characterRequests = Physics::CharacterRequestBus::FindFirstHandler(GetEntityId());
m_physicsCharacter = (characterRequests != nullptr) ? characterRequests->GetCharacter() : nullptr;
GetNetBindComponent()->AddEntitySyncRewindEventHandler(m_syncRewindHandler);
if (m_physicsCharacter)
{
auto controller = static_cast<PhysX::CharacterController*>(m_physicsCharacter);
controller->SetFilterFlags(physx::PxQueryFlag::eSTATIC | physx::PxQueryFlag::eDYNAMIC | physx::PxQueryFlag::ePREFILTER);
if (auto callbackManager = controller->GetCallbackManager())
{
callbackManager->SetControllerFilter(CollisionLayerBasedControllerFilter);
callbackManager->SetObjectPreFilter(CollisionLayerBasedObjectPreFilter);
}
}
if (!HasController())
{
GetNetworkTransformComponent()->TranslationAddEvent(m_translationEventHandler);
}
}
void NetworkCharacterComponent::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
{
;
}
void NetworkCharacterComponent::OnTranslationChangedEvent([[maybe_unused]] const AZ::Vector3& translation)
{
OnSyncRewind();
}
void NetworkCharacterComponent::OnSyncRewind()
{
if (m_physicsCharacter == nullptr)
{
return;
}
const AZ::Vector3 currPosition = m_physicsCharacter->GetBasePosition();
if (!currPosition.IsClose(GetNetworkTransformComponent()->GetTranslation()))
{
uint32_t frameId = static_cast<uint32_t>(Multiplayer::GetNetworkTime()->GetHostFrameId());
m_physicsCharacter->SetFrameId(frameId);
//m_physicsCharacter->SetBasePosition(GetNetworkTransformComponent()->GetTranslation());
}
}
bool NetworkCharacterComponent::IsOnGround() const
{
auto pxController = static_cast<physx::PxController*>(m_physicsCharacter->GetNativePointer());
if (!pxController)
{
return true;
}
physx::PxControllerState state;
pxController->getState(state);
return state.touchedActor != nullptr || (state.collisionFlags & physx::PxControllerCollisionFlag::eCOLLISION_DOWN) != 0;
}
NetworkCharacterComponentController::NetworkCharacterComponentController(NetworkCharacterComponent& parent)
: NetworkCharacterComponentControllerBase(parent)
{
;
}
void NetworkCharacterComponentController::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
{
;
}
void NetworkCharacterComponentController::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
{
;
}
AZ::Vector3 NetworkCharacterComponentController::TryMoveWithVelocity(const AZ::Vector3& velocity, [[maybe_unused]] float deltaTime)
{
// Ensure any entities that we might interact with are properly synchronized to their rewind state
if (IsAuthority())
{
const AZ::Aabb entityStartBounds = AZ::Interface<AzFramework::IEntityBoundsUnion>::Get()->GetEntityLocalBoundsUnion(GetEntity()->GetId());
const AZ::Aabb entityFinalBounds = entityStartBounds.GetTranslated(velocity);
AZ::Aabb entitySweptBounds = entityStartBounds;
entitySweptBounds.AddAabb(entityFinalBounds);
Multiplayer::GetNetworkTime()->SyncEntitiesToRewindState(entitySweptBounds);
}
if ((GetParent().m_physicsCharacter == nullptr) || (velocity.GetLengthSq() <= 0.0f))
{
return GetEntity()->GetTransform()->GetWorldTranslation();
}
GetParent().m_physicsCharacter->AddVelocity(velocity);
GetParent().m_physicsCharacter->ApplyRequestedVelocity(deltaTime);
GetEntity()->GetTransform()->SetWorldTranslation(GetParent().m_physicsCharacter->GetBasePosition());
AZLOG
(
NET_Movement,
"Moved to position %f x %f x %f",
GetParent().m_physicsCharacter->GetBasePosition().GetX(),
GetParent().m_physicsCharacter->GetBasePosition().GetY(),
GetParent().m_physicsCharacter->GetBasePosition().GetZ()
);
return GetEntity()->GetTransform()->GetWorldTranslation();
}
}
@@ -0,0 +1,221 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <Multiplayer/Components/NetworkHitVolumesComponent.h>
#include <AzFramework/Components/TransformComponent.h>
#include <AzFramework/Physics/Common/PhysicsTypes.h>
#include <AzFramework/Physics/CharacterBus.h>
#include <AzFramework/Physics/Character.h>
#include <AzFramework/Physics/SystemBus.h>
#include <MCore/Source/AzCoreConversions.h>
#include <Integration/ActorComponentBus.h>
namespace Multiplayer
{
AZ_CVAR(bool, bg_DrawArticulatedHitVolumes, false, nullptr, AZ::ConsoleFunctorFlags::Null, "Enables debug draw of articulated hit volumes");
AZ_CVAR(float, bg_DrawDebugHitVolumeLifetime, 0.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "The lifetime for hit volume draw-debug shapes");
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");
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");
NetworkHitVolumesComponent::AnimatedHitVolume::AnimatedHitVolume
(
AzNetworking::ConnectionId connectionId,
Physics::CharacterRequests* character,
const char* hitVolumeName,
const Physics::ColliderConfiguration* colliderConfig,
const Physics::ShapeConfiguration* shapeConfig,
const uint32_t jointIndex
)
: m_colliderConfig(colliderConfig)
, m_shapeConfig(shapeConfig)
, m_jointIndex(jointIndex)
{
m_transform.SetOwningConnectionId(connectionId);
m_colliderOffSetTransform = AZ::Transform::CreateFromQuaternionAndTranslation(m_colliderConfig->m_rotation, m_colliderConfig->m_position);
if (m_colliderConfig->m_isExclusive)
{
Physics::SystemRequestBus::BroadcastResult(m_physicsShape, &Physics::SystemRequests::CreateShape, *m_colliderConfig, *m_shapeConfig);
}
else
{
Physics::ColliderConfiguration colliderConfiguration = *m_colliderConfig;
colliderConfiguration.m_isExclusive = true;
colliderConfiguration.m_isSimulated = false;
colliderConfiguration.m_isInSceneQueries = true;
Physics::SystemRequestBus::BroadcastResult(m_physicsShape, &Physics::SystemRequests::CreateShape, colliderConfiguration, *m_shapeConfig);
}
if (m_physicsShape)
{
m_physicsShape->SetName(hitVolumeName);
character->GetCharacter()->AttachShape(m_physicsShape);
}
}
void NetworkHitVolumesComponent::AnimatedHitVolume::UpdateTransform(const AZ::Transform& transform)
{
m_transform = transform;
m_physicsShape->SetLocalPose(transform.GetTranslation(), transform.GetRotation());
}
void NetworkHitVolumesComponent::AnimatedHitVolume::SyncToCurrentTransform()
{
AZ::Transform rewoundTransform;
const AZ::Transform& targetTransform = m_transform.Get();
const float blendFactor = Multiplayer::GetNetworkTime()->GetHostBlendFactor();
if (blendFactor < 1.f)
{
// If a blend factor was supplied, interpolate the transform appropriately
const AZ::Transform& previousTransform = m_transform.GetPrevious();
rewoundTransform.SetRotation(previousTransform.GetRotation().Slerp(targetTransform.GetRotation(), blendFactor));
rewoundTransform.SetTranslation(previousTransform.GetTranslation().Lerp(targetTransform.GetTranslation(), blendFactor));
rewoundTransform.SetUniformScale(AZ::Lerp(previousTransform.GetUniformScale(), targetTransform.GetUniformScale(), blendFactor));
}
else
{
rewoundTransform = m_transform.Get();
}
const AZ::Transform physicsTransform = AZ::Transform::CreateFromQuaternionAndTranslation(m_physicsShape->GetLocalPose().second, m_physicsShape->GetLocalPose().first);
// Don't call SetLocalPose unless the transforms are actually different
const AZ::Vector3 positionDelta = physicsTransform.GetTranslation() - rewoundTransform.GetTranslation();
const AZ::Quaternion orientationDelta = physicsTransform.GetRotation() - rewoundTransform.GetRotation();
if ((positionDelta.GetLengthSq() >= bg_RewindPositionTolerance) || (orientationDelta.GetLengthSq() >= bg_RewindOrientationTolerance))
{
m_physicsShape->SetLocalPose(rewoundTransform.GetTranslation(), rewoundTransform.GetRotation());
}
}
void NetworkHitVolumesComponent::NetworkHitVolumesComponent::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<NetworkHitVolumesComponent, NetworkHitVolumesComponentBase>()
->Version(1);
}
NetworkHitVolumesComponentBase::Reflect(context);
}
NetworkHitVolumesComponent::NetworkHitVolumesComponent()
: m_syncRewindHandler([this]() { OnSyncRewind(); })
, m_preRenderHandler([this](float deltaTime, float blendFactor) { OnPreRender(deltaTime, blendFactor); })
, m_transformChangedHandler([this](const AZ::Transform&, const AZ::Transform& worldTm) { OnTransformUpdate(worldTm); })
{
;
}
void NetworkHitVolumesComponent::OnInit()
{
;
}
void NetworkHitVolumesComponent::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
{
EMotionFX::Integration::ActorComponentNotificationBus::Handler::BusConnect(GetEntityId());
GetNetBindComponent()->AddEntitySyncRewindEventHandler(m_syncRewindHandler);
m_physicsCharacter = Physics::CharacterRequestBus::FindFirstHandler(GetEntityId());
GetTransformComponent()->BindTransformChangedEventHandler(m_transformChangedHandler);
OnTransformUpdate(GetTransformComponent()->GetWorldTM());
}
void NetworkHitVolumesComponent::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
{
DestroyHitVolumes();
EMotionFX::Integration::ActorComponentNotificationBus::Handler::BusDisconnect();
}
void NetworkHitVolumesComponent::OnPreRender([[maybe_unused]] float deltaTime, [[maybe_unused]] float blendFactor)
{
if (m_animatedHitVolumes.size() <= 0)
{
CreateHitVolumes();
}
AZ::Vector3 position, scale;
AZ::Quaternion rotation;
for (AnimatedHitVolume& hitVolume : m_animatedHitVolumes)
{
m_actorComponent->GetJointTransformComponents(hitVolume.m_jointIndex, EMotionFX::Integration::Space::ModelSpace, position, rotation, scale);
hitVolume.UpdateTransform(AZ::Transform::CreateFromQuaternionAndTranslation(rotation, position) * hitVolume.m_colliderOffSetTransform);
}
}
void NetworkHitVolumesComponent::OnTransformUpdate([[maybe_unused]] const AZ::Transform& transform)
{
OnSyncRewind();
}
void NetworkHitVolumesComponent::OnSyncRewind()
{
if (m_physicsCharacter && m_physicsCharacter->GetCharacter())
{
uint32_t frameId = static_cast<uint32_t>(Multiplayer::GetNetworkTime()->GetHostFrameId());
m_physicsCharacter->GetCharacter()->SetFrameId(frameId);
}
for (AnimatedHitVolume& hitVolume : m_animatedHitVolumes)
{
hitVolume.SyncToCurrentTransform();
}
}
void NetworkHitVolumesComponent::CreateHitVolumes()
{
if (m_physicsCharacter == nullptr || m_actorComponent == nullptr)
{
return;
}
const Physics::AnimationConfiguration* physicsConfig = m_actorComponent->GetPhysicsConfig();
if (physicsConfig == nullptr)
{
return;
}
m_hitDetectionConfig = &physicsConfig->m_hitDetectionConfig;
const AzNetworking::ConnectionId owningConnectionId = GetNetBindComponent()->GetOwningConnectionId();
m_animatedHitVolumes.reserve(m_hitDetectionConfig->m_nodes.size());
for (const Physics::CharacterColliderNodeConfiguration& nodeConfig : m_hitDetectionConfig->m_nodes)
{
const AZStd::size_t jointIndex = m_actorComponent->GetJointIndexByName(nodeConfig.m_name.c_str());
if (jointIndex == EMotionFX::Integration::ActorComponentRequests::s_invalidJointIndex)
{
continue;
}
for (const AzPhysics::ShapeColliderPair& coliderPair : nodeConfig.m_shapes)
{
const Physics::ColliderConfiguration* colliderConfig = coliderPair.first.get();
Physics::ShapeConfiguration* shapeConfig = coliderPair.second.get();
m_animatedHitVolumes.emplace_back(owningConnectionId, m_physicsCharacter, nodeConfig.m_name.c_str(), colliderConfig, shapeConfig, aznumeric_cast<uint32_t>(jointIndex));
}
}
}
void NetworkHitVolumesComponent::DestroyHitVolumes()
{
m_animatedHitVolumes.clear();
}
void NetworkHitVolumesComponent::OnActorInstanceCreated([[maybe_unused]] EMotionFX::ActorInstance* actorInstance)
{
m_actorComponent = EMotionFX::Integration::ActorComponentRequestBus::FindFirstHandler(GetEntity()->GetId());
}
void NetworkHitVolumesComponent::OnActorInstanceDestroyed([[maybe_unused]] EMotionFX::ActorInstance* actorInstance)
{
m_actorComponent = nullptr;
}
}
@@ -0,0 +1,147 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <Multiplayer/Components/NetworkRigidBodyComponent.h>
#include <AzFramework/Components/TransformComponent.h>
#include <AzFramework/Physics/RigidBodyBus.h>
#include <AzFramework/Physics/SimulatedBodies/RigidBody.h>
namespace Multiplayer
{
AZ_CVAR_EXTERNED(float, bg_RewindPositionTolerance);
AZ_CVAR_EXTERNED(float, bg_RewindOrientationTolerance);
void NetworkRigidBodyComponent::NetworkRigidBodyComponent::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<NetworkRigidBodyComponent, NetworkRigidBodyComponentBase>()->Version(1);
}
NetworkRigidBodyComponentBase::Reflect(context);
}
void NetworkRigidBodyComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC_CE("NetworkRigidBodyService"));
}
void NetworkRigidBodyComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{
required.push_back(AZ_CRC_CE("PhysXRigidBodyService"));
}
void NetworkRigidBodyComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)
{
dependent.push_back(AZ_CRC_CE("TransformService"));
dependent.push_back(AZ_CRC_CE("PhysXRigidBodyService"));
}
NetworkRigidBodyComponent::NetworkRigidBodyComponent()
: m_syncRewindHandler([this](){ OnSyncRewind(); })
, m_transformChangedHandler([this]([[maybe_unused]] const AZ::Transform& localTm, const AZ::Transform& worldTm){ OnTransformUpdate(worldTm); })
{
}
void NetworkRigidBodyComponent::OnInit()
{
}
void NetworkRigidBodyComponent::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
{
NetworkRigidBodyRequestBus::Handler::BusConnect(GetEntityId());
GetNetBindComponent()->AddEntitySyncRewindEventHandler(m_syncRewindHandler);
GetEntity()->FindComponent<AzFramework::TransformComponent>()->BindTransformChangedEventHandler(m_transformChangedHandler);
m_physicsRigidBodyComponent =
Physics::RigidBodyRequestBus::FindFirstHandler(GetEntity()->GetId());
AZ_Assert(m_physicsRigidBodyComponent, "PhysX Rigid Body Component is required on entity %s", GetEntity()->GetName().c_str());
if (!HasController())
{
m_physicsRigidBodyComponent->SetKinematic(true);
}
}
void NetworkRigidBodyComponent::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
{
NetworkRigidBodyRequestBus::Handler::BusDisconnect();
}
void NetworkRigidBodyComponent::OnTransformUpdate(const AZ::Transform& worldTm)
{
m_transform = worldTm;
if (!HasController())
{
m_physicsRigidBodyComponent->SetKinematicTarget(worldTm);
}
}
void NetworkRigidBodyComponent::OnSyncRewind()
{
uint32_t frameId = static_cast<uint32_t>(Multiplayer::GetNetworkTime()->GetHostFrameId());
AzPhysics::RigidBody* rigidBody = m_physicsRigidBodyComponent->GetRigidBody();
rigidBody->SetFrameId(frameId);
AZ::Transform rewoundTransform;
const AZ::Transform& targetTransform = m_transform.Get();
const float blendFactor = Multiplayer::GetNetworkTime()->GetHostBlendFactor();
if (blendFactor < 1.f)
{
// If a blend factor was supplied, interpolate the transform appropriately
const AZ::Transform& previousTransform = m_transform.GetPrevious();
rewoundTransform.SetRotation(previousTransform.GetRotation().Slerp(targetTransform.GetRotation(), blendFactor));
rewoundTransform.SetTranslation(previousTransform.GetTranslation().Lerp(targetTransform.GetTranslation(), blendFactor));
rewoundTransform.SetUniformScale(AZ::Lerp(previousTransform.GetUniformScale(), targetTransform.GetUniformScale(), blendFactor));
}
else
{
rewoundTransform = m_transform.Get();
}
const AZ::Transform& physicsTransform = rigidBody->GetTransform();
// Don't call SetLocalPose unless the transforms are actually different
const AZ::Vector3 positionDelta = physicsTransform.GetTranslation() - rewoundTransform.GetTranslation();
const AZ::Quaternion orientationDelta = physicsTransform.GetRotation() - rewoundTransform.GetRotation();
if ((positionDelta.GetLengthSq() >= bg_RewindPositionTolerance) ||
(orientationDelta.GetLengthSq() >= bg_RewindOrientationTolerance))
{
rigidBody->SetTransform(rewoundTransform);
}
}
NetworkRigidBodyComponentController::NetworkRigidBodyComponentController(NetworkRigidBodyComponent& parent)
: NetworkRigidBodyComponentControllerBase(parent)
{
;
}
void NetworkRigidBodyComponentController::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
{
;
}
void NetworkRigidBodyComponentController::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
{
;
}
void NetworkRigidBodyComponentController::HandleSendApplyImpulse
(
[[maybe_unused]] AzNetworking::IConnection* invokingConnection,
const AZ::Vector3& impulse,
const AZ::Vector3& worldPoint
)
{
AzPhysics::RigidBody* rigidBody = GetParent().m_physicsRigidBodyComponent->GetRigidBody();
rigidBody->ApplyLinearImpulseAtWorldPoint(impulse, worldPoint);
}
} // namespace Multiplayer
@@ -18,6 +18,9 @@ set(FILES
Include/Multiplayer/Components/MultiplayerController.h
Include/Multiplayer/Components/MultiplayerComponentRegistry.h
Include/Multiplayer/Components/NetBindComponent.h
Include/Multiplayer/Components/NetworkCharacterComponent.h
Include/Multiplayer/Components/NetworkHitVolumesComponent.h
Include/Multiplayer/Components/NetworkRigidBodyComponent.h
Include/Multiplayer/Components/NetworkTransformComponent.h
Include/Multiplayer/ConnectionData/IConnectionData.h
Include/Multiplayer/EntityDomains/IEntityDomain.h
@@ -54,12 +57,18 @@ set(FILES
Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.xml
Source/AutoGen/Multiplayer.AutoPackets.xml
Source/AutoGen/MultiplayerEditor.AutoPackets.xml
Source/AutoGen/NetworkCharacterComponent.AutoComponent.xml
Source/AutoGen/NetworkHitVolumesComponent.AutoComponent.xml
Source/AutoGen/NetworkRigidBodyComponent.AutoComponent.xml
Source/AutoGen/NetworkTransformComponent.AutoComponent.xml
Source/Components/LocalPredictionPlayerInputComponent.cpp
Source/Components/MultiplayerComponent.cpp
Source/Components/MultiplayerController.cpp
Source/Components/MultiplayerComponentRegistry.cpp
Source/Components/NetBindComponent.cpp
Source/Components/NetworkCharacterComponent.cpp
Source/Components/NetworkHitVolumesComponent.cpp
Source/Components/NetworkRigidBodyComponent.cpp
Source/Components/NetworkTransformComponent.cpp
Source/ConnectionData/ClientToServerConnectionData.cpp
Source/ConnectionData/ClientToServerConnectionData.h