/* * 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 #include #include #include #include #include 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; /// 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; /// 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; /// Called when the controller collides with another object. using OnShapeHit = AZStd::function; /// Called when the controller collides with another controller. using OnControllerHit = AZStd::function; /// Called when the controller collides with an obstacle. using OnObstacleHit = AZStd::function; /// Determines whether the controller should be able to ride on other objects or should slide. using ObjectRidingBehavior = AZStd::function; /// Determines whether the controller should slide when standing on another character. using ControllerRidingBehavior = AZStd::function; /// Determines whether the controller should be able to ride on obstacles or should slide. using ObstacleRidingBehavior = AZStd::function; /// 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 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 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 m_material; ///< The generic physics API material for the controller. AZStd::shared_ptr 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; //! m_callbackManager; ///< Manages callbacks for collision filtering, collision notifications, and handling riding on objects. }; } // namespace PhysX