You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
251 lines
14 KiB
C++
251 lines
14 KiB
C++
/*
|
|
* 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 <AzFramework/Physics/Character.h>
|
|
#include <PhysX/UserDataTypes.h>
|
|
#include <PxPhysicsAPI.h>
|
|
|
|
#include <AzFramework/Physics/Collision/CollisionGroups.h>
|
|
#include <AzFramework/Physics/Collision/CollisionLayers.h>
|
|
#include <AzFramework/Physics/Common/PhysicsTypes.h>
|
|
|
|
namespace PhysX
|
|
{
|
|
static const float epsilon = 1e-3f;
|
|
|
|
enum class SlopeBehaviour
|
|
{
|
|
PreventClimbing,
|
|
ForceSliding
|
|
};
|
|
|
|
/// Allows PhysX specific character controller properties that are not included in the generic configuration.
|
|
class CharacterControllerConfiguration
|
|
: public Physics::CharacterConfiguration
|
|
{
|
|
public:
|
|
AZ_CLASS_ALLOCATOR(CharacterControllerConfiguration, AZ::SystemAllocator, 0);
|
|
AZ_RTTI(CharacterControllerConfiguration, "{23A8DFD6-7DA4-4CB3-BBD3-7FB58DEE6F9D}", Physics::CharacterConfiguration);
|
|
static void Reflect(AZ::ReflectContext* context);
|
|
|
|
CharacterControllerConfiguration() = default;
|
|
CharacterControllerConfiguration(const CharacterControllerConfiguration&) = default;
|
|
virtual ~CharacterControllerConfiguration() = default;
|
|
|
|
SlopeBehaviour m_slopeBehaviour = SlopeBehaviour::PreventClimbing; ///< Behaviour on surfaces above maximum slope.
|
|
float m_contactOffset = 0.1f; ///< Extra distance outside the controller used to give smoother contact resolution.
|
|
float m_scaleCoefficient = 0.8f; ///< Scalar coefficient used to scale the controller, usually slightly smaller than 1.
|
|
};
|
|
|
|
/// Manages callbacks for character controller collision filtering, collision notifications, and handling riding on objects.
|
|
class CharacterControllerCallbackManager
|
|
: public physx::PxControllerFilterCallback
|
|
, public physx::PxQueryFilterCallback
|
|
, public physx::PxUserControllerHitReport
|
|
, public physx::PxControllerBehaviorCallback
|
|
{
|
|
public:
|
|
AZ_CLASS_ALLOCATOR(CharacterControllerCallbackManager, AZ::SystemAllocator, 0);
|
|
AZ_TYPE_INFO(CharacterControllerCallbackManager, "93C7DEA8-98E6-4C07-96B7-D215800D0ECB");
|
|
|
|
/// Determines whether this controller should be obstructed by other controllers or able to move through them.
|
|
using ControllerFilter = AZStd::function<bool(const physx::PxController&, const physx::PxController&)>;
|
|
|
|
/// Called when another object has been identified as potentially obstructing the controller's path, but before
|
|
/// an exact intersection test has been performed (if the ePREFILTER flag is set in the controller's filter flags).
|
|
using ObjectPreFilter = AZStd::function<physx::PxQueryHitType::Enum(const physx::PxFilterData&,
|
|
const physx::PxShape*,const physx::PxRigidActor*, physx::PxHitFlags&)>;
|
|
|
|
/// Called after an exact intersection test has identified another object as obstructing the controller's path
|
|
/// (if the ePOSTFILTER flag is set in the controller's filter flags).
|
|
using ObjectPostFilter = AZStd::function<physx::PxQueryHitType::Enum(const physx::PxFilterData&,
|
|
const physx::PxQueryHit&)>;
|
|
|
|
/// Called when the controller collides with another object.
|
|
using OnShapeHit = AZStd::function<void(const physx::PxControllerShapeHit&)>;
|
|
|
|
/// Called when the controller collides with another controller.
|
|
using OnControllerHit = AZStd::function<void(const physx::PxControllersHit&)>;
|
|
|
|
/// Called when the controller collides with an obstacle.
|
|
using OnObstacleHit = AZStd::function<void(const physx::PxControllerObstacleHit&)>;
|
|
|
|
/// Determines whether the controller should be able to ride on other objects or should slide.
|
|
using ObjectRidingBehavior = AZStd::function<physx::PxControllerBehaviorFlags(const physx::PxShape&,
|
|
const physx::PxActor&)>;
|
|
|
|
/// Determines whether the controller should slide when standing on another character.
|
|
using ControllerRidingBehavior = AZStd::function<physx::PxControllerBehaviorFlags(const physx::PxController&)>;
|
|
|
|
/// Determines whether the controller should be able to ride on obstacles or should slide.
|
|
using ObstacleRidingBehavior = AZStd::function<physx::PxControllerBehaviorFlags(const physx::PxObstacle&)>;
|
|
|
|
/// Sets the function which determines whether this controller should be obstructed by other controllers or
|
|
/// able to move through them.
|
|
void SetControllerFilter(ControllerFilter controllerFilter);
|
|
|
|
/// Sets the function which is called when another object has been identified as potentially obstructing the
|
|
/// controller's path, but before an exact intersection test has been performed.
|
|
/// The function will only be called if the ePREFILTER flag is set in the controller's filter flags.
|
|
void SetObjectPreFilter(ObjectPreFilter objectPreFilter);
|
|
|
|
/// Sets the function which is called after an exact intersection test has identified another object as
|
|
/// obstructing the controller's path.
|
|
/// The function will only be called if the ePOSTFILTER flag is set in the controller's filter flags.
|
|
void SetObjectPostFilter(ObjectPostFilter objectPostFilter);
|
|
|
|
/// Sets the function which is called when the controller collides with another object.
|
|
void SetOnShapeHit(OnShapeHit onShapeHit);
|
|
|
|
/// Sets the function which is called when the controller collides with another controller.
|
|
void SetOnControllerHit(OnControllerHit onControllerHit);
|
|
|
|
/// Sets the function which is called when the controller collides with an obstacle.
|
|
void SetOnObstacleHit(OnObstacleHit onObstacleHit);
|
|
|
|
/// Sets the function which determines whether the controller should be able to ride on other objects or should slide.
|
|
void SetObjectRidingBehavior(ObjectRidingBehavior objectRidingBehavior);
|
|
|
|
/// Sets the function which determines whether the controller should slide when standing on another character.
|
|
void SetControllerRidingBehavior(ControllerRidingBehavior controllerRidingBehavior);
|
|
|
|
/// Sets the function which determines whether the controller should be able to ride on obstacles or should slide.
|
|
void SetObstacleRidingBehavior(ObstacleRidingBehavior obstacleRidingBehavior);
|
|
|
|
// physx::PxControllerFilterCallback
|
|
bool filter(const physx::PxController& controllerA, const physx::PxController& controllerB) override;
|
|
|
|
// physx::PxQueryFilterCallback
|
|
physx::PxQueryHitType::Enum preFilter(const physx::PxFilterData& filterData, const physx::PxShape* shape,
|
|
const physx::PxRigidActor* actor, physx::PxHitFlags& queryFlags) override;
|
|
physx::PxQueryHitType::Enum postFilter(const physx::PxFilterData& filterData, const physx::PxQueryHit& hit) override;
|
|
|
|
// physx::PxUserControllerHitReport
|
|
void onShapeHit(const physx::PxControllerShapeHit& hit) override;
|
|
void onControllerHit(const physx::PxControllersHit& hit) override;
|
|
void onObstacleHit(const physx::PxControllerObstacleHit& hit) override;
|
|
|
|
// physx::PxControllerBehaviorCallback
|
|
physx::PxControllerBehaviorFlags getBehaviorFlags(const physx::PxShape& shape, const physx::PxActor& actor) override;
|
|
physx::PxControllerBehaviorFlags getBehaviorFlags(const physx::PxController& controller) override;
|
|
physx::PxControllerBehaviorFlags getBehaviorFlags(const physx::PxObstacle& obstacle) override;
|
|
|
|
private:
|
|
ControllerFilter m_controllerFilter;
|
|
ObjectPreFilter m_objectPreFilter;
|
|
ObjectPostFilter m_objectPostFilter;
|
|
OnShapeHit m_onShapeHit;
|
|
OnControllerHit m_onControllerHit;
|
|
OnObstacleHit m_onObstacleHit;
|
|
ObjectRidingBehavior m_objectRidingBehavior;
|
|
ControllerRidingBehavior m_controllerRidingBehavior;
|
|
ObstacleRidingBehavior m_obstacleRidingBehavior;
|
|
};
|
|
|
|
class CharacterController
|
|
: public Physics::Character
|
|
{
|
|
public:
|
|
AZ_CLASS_ALLOCATOR(CharacterController, AZ::SystemAllocator, 0);
|
|
AZ_RTTI(PhysX::CharacterController, "{A75A7D19-BC21-4F7E-A3D9-05031D2DFC94}", Physics::Character);
|
|
static void Reflect(AZ::ReflectContext* context);
|
|
|
|
CharacterController() = default;
|
|
CharacterController(physx::PxController* pxController,
|
|
AZStd::unique_ptr<CharacterControllerCallbackManager> callbackManager,
|
|
AzPhysics::SceneHandle sceneHandle);
|
|
~CharacterController();
|
|
|
|
//! Character Controller can be only enabled and disabled once after creation.
|
|
//! And after being disabled it cannot be enabled again, it has to be destroyed and re-created.
|
|
//! This is because the way PhysX controller works, it doesn't allow the state of having physics disabled, so being enabled/disabled is linked to be created/destroyed.
|
|
//! @{
|
|
void EnablePhysics(const Physics::CharacterConfiguration& configuration);
|
|
void DisablePhysics();
|
|
//! @}
|
|
|
|
CharacterControllerCallbackManager* GetCallbackManager();
|
|
void SetFilterFlags(physx::PxQueryFlags filterFlags);
|
|
|
|
// Physics::Character
|
|
AZ::Vector3 GetBasePosition() const override;
|
|
void SetBasePosition(const AZ::Vector3& position) override;
|
|
AZ::Vector3 GetCenterPosition() const override;
|
|
float GetStepHeight() const override;
|
|
void SetStepHeight(float stepHeight) override;
|
|
AZ::Vector3 GetUpDirection() const override;
|
|
void SetUpDirection(const AZ::Vector3& upDirection) override;
|
|
float GetSlopeLimitDegrees() const override;
|
|
void SetSlopeLimitDegrees(float slopeLimitDegrees) override;
|
|
float GetMaximumSpeed() const override;
|
|
void SetMaximumSpeed(float maximumSpeed) override;
|
|
AZ::Vector3 GetVelocity() const override;
|
|
AzPhysics::CollisionLayer GetCollisionLayer() const override;
|
|
AzPhysics::CollisionGroup GetCollisionGroup() const override;
|
|
void SetCollisionLayer(const AzPhysics::CollisionLayer& layer) override;
|
|
void SetCollisionGroup(const AzPhysics::CollisionGroup& group) override;
|
|
AZ::Crc32 GetColliderTag() const override;
|
|
void AddVelocity(const AZ::Vector3& velocity) override;
|
|
void ApplyRequestedVelocity(float deltaTime) override;
|
|
void SetRotation(const AZ::Quaternion& rotation) override;
|
|
void AttachShape(AZStd::shared_ptr<Physics::Shape> shape) override;
|
|
|
|
// AzPhysics::SimulatedBody
|
|
AZ::EntityId GetEntityId() const override;
|
|
AzPhysics::Scene* GetScene() override;
|
|
AZ::Transform GetTransform() const override;
|
|
void SetTransform(const AZ::Transform& transform) override;
|
|
AZ::Vector3 GetPosition() const override;
|
|
AZ::Quaternion GetOrientation() const override;
|
|
AZ::Aabb GetAabb() const override;
|
|
AzPhysics::SceneQueryHit RayCast(const AzPhysics::RayCastRequest& request) override;
|
|
AZ::Crc32 GetNativeType() const override;
|
|
void* GetNativePointer() const override;
|
|
|
|
// CharacterController specific
|
|
void Resize(float height);
|
|
float GetHeight() const;
|
|
void SetHeight(float height);
|
|
float GetRadius() const;
|
|
void SetRadius(float radius);
|
|
float GetHalfSideExtent() const;
|
|
void SetHalfSideExtent(float halfSideExtent);
|
|
float GetHalfForwardExtent() const;
|
|
void SetHalfForwardExtent(float halfForwardExtent);
|
|
|
|
private:
|
|
void SetFilterDataAndShape(const Physics::CharacterConfiguration& characterConfig);
|
|
void SetUserData(const Physics::CharacterConfiguration& characterConfig);
|
|
void SetActorName(const AZStd::string& name = "Character Controller");
|
|
void SetMinimumMovementDistance(float distance);
|
|
void SetTag(const AZStd::string& tag);
|
|
void CreateShadowBody(const Physics::CharacterConfiguration& configuration);
|
|
void DestroyShadowBody();
|
|
void RemoveControllerFromScene();
|
|
void UpdateFilterLayerAndGroup(AzPhysics::CollisionLayer collisionLayer, AzPhysics::CollisionGroup collisionGroup);
|
|
|
|
physx::PxController* m_pxController = nullptr; ///< The underlying PhysX controller.
|
|
float m_minimumMovementDistance = 0.0f; ///< To avoid jittering, the controller will not attempt to move distances below this.
|
|
AZ::Vector3 m_requestedVelocity = AZ::Vector3::CreateZero(); ///< Used to accumulate velocity requests during a tick.
|
|
AZ::Vector3 m_observedVelocity = AZ::Vector3::CreateZero(); ///< Velocity observed in the simulation, may not match desired.
|
|
PhysX::ActorData m_actorUserData; ///< Used to populate the user data on the PxActor associated with the controller.
|
|
physx::PxFilterData m_filterData; ///< Controls filtering for collisions with other objects and scene queries.
|
|
physx::PxControllerFilters m_pxControllerFilters; ///< Controls which objects the controller interacts with when moving.
|
|
AZStd::shared_ptr<Physics::Material> m_material; ///< The generic physics API material for the controller.
|
|
AZStd::shared_ptr<Physics::Shape> m_shape; ///< The generic physics API shape associated with the controller.
|
|
AzPhysics::RigidBody* m_shadowBody = nullptr; ///< A kinematic-synchronised rigid body used to store additional colliders.
|
|
AzPhysics::SimulatedBodyHandle m_shadowBodyHandle = AzPhysics::InvalidSimulatedBodyHandle; //!<A handle to the shadow body.
|
|
AZStd::string m_name = "Character Controller"; ///< Name to set on the PhysX actor associated with the controller.
|
|
AZ::Crc32 m_colliderTag; ///< Tag used to identify the collider associated with the controller.
|
|
float m_maximumSpeed = 100.0f; ///< If the accumulated requested velocity for a tick exceeds this magnitude, it will be clamped.
|
|
AZStd::unique_ptr<CharacterControllerCallbackManager>
|
|
m_callbackManager; ///< Manages callbacks for collision filtering, collision notifications, and handling riding on objects.
|
|
};
|
|
} // namespace PhysX
|