Browse Source

PhysX implementation of a character controller

BearishSun 9 years ago
parent
commit
701b395ddf

+ 142 - 35
BansheeCore/Include/BsCharacterController.h

@@ -14,7 +14,7 @@ namespace BansheeEngine
 	 */
 	 */
 	enum class CharacterClimbingMode
 	enum class CharacterClimbingMode
 	{
 	{
-		Normal, /**< Normal behaviour. Capsule character controller will be able to auto-step over the step offset. */
+		Normal, /**< Normal behaviour. Capsule character controller will be able to auto-step even above the step offset. */
 		Constrained /**< The system will attempt to limit auto-step to the provided step offset and no higher. */
 		Constrained /**< The system will attempt to limit auto-step to the provided step offset and no higher. */
 	};
 	};
 
 
@@ -37,14 +37,26 @@ namespace BansheeEngine
 	typedef Flags<CharacterCollisionFlag> CharacterCollisionFlags;
 	typedef Flags<CharacterCollisionFlag> CharacterCollisionFlags;
 	BS_FLAGS_OPERATORS(CharacterCollisionFlag)
 	BS_FLAGS_OPERATORS(CharacterCollisionFlag)
 
 
+	struct CHAR_CONTROLLER_DESC;
+	struct ControllerColliderCollision;
+	struct ControllerControllerCollision;
+
+	/** 
+	 * Special physics controller meant to be used for game characters. Uses the "slide-and-collide" physics instead of
+	 * of the standard physics model to handle various issues with manually moving kinematic objects. Uses a capsule to
+	 * represent the character's bounds. 
+	 */
 	class BS_CORE_EXPORT CharacterController
 	class BS_CORE_EXPORT CharacterController
 	{
 	{
 	public:
 	public:
+		CharacterController(const CHAR_CONTROLLER_DESC& desc) { }
 		virtual ~CharacterController() { }
 		virtual ~CharacterController() { }
 
 
 		/**
 		/**
 		 * Moves the controller in the specified direction by the specified amount, while interacting with surrounding
 		 * Moves the controller in the specified direction by the specified amount, while interacting with surrounding
 		 * geometry. Returns flags signaling where collision occurred after the movement.
 		 * geometry. Returns flags signaling where collision occurred after the movement.
+		 *
+		 * Does not account for gravity, you must apply it manually.
 		 */
 		 */
 		virtual CharacterCollisionFlags move(const Vector3& displacement) = 0;
 		virtual CharacterCollisionFlags move(const Vector3& displacement) = 0;
 
 
@@ -72,10 +84,10 @@ namespace BansheeEngine
 		/** Sets the radius of the controller capsule. */
 		/** Sets the radius of the controller capsule. */
 		virtual void setRadius(float radius) = 0;
 		virtual void setRadius(float radius) = 0;
 
 
-		/** Returns the height of the controller capsule. */
+		/** Returns the height between the centers of the two spheres of the controller capsule. */
 		virtual float getHeight() const = 0;
 		virtual float getHeight() const = 0;
 
 
-		/** Sets the height of the controller capsule. */
+		/** Sets the height between the centers of the two spheres of the controller capsule. */
 		virtual void setHeight(float height) = 0;
 		virtual void setHeight(float height) = 0;
 
 
 		/** Returns the up direction of capsule. Determines capsule orientation. */
 		/** Returns the up direction of capsule. Determines capsule orientation. */
@@ -85,86 +97,181 @@ namespace BansheeEngine
 		virtual void setUp(const Vector3& up) = 0;
 		virtual void setUp(const Vector3& up) = 0;
 
 
 		/** 
 		/** 
-		 * Returns climbing mode that controls what happens when character encounters a height higher than its step offset. 
+		 * Returns climbing mode.
 		 *
 		 *
-		 * @see	CharacterClimbingMode
+		 * @copydoc	CHAR_CONTROLLER_DESC::climbingMode
 		 */
 		 */
 		virtual CharacterClimbingMode getClimbingMode() const = 0;
 		virtual CharacterClimbingMode getClimbingMode() const = 0;
 
 
 		/** 
 		/** 
-		 * Sets climbing mode that controls what happens when character encounters a height higher than its step offset. 
+		 * Sets climbing mode.
 		 *
 		 *
-		 * @see	CharacterClimbingMode
+		 * @copydoc	CHAR_CONTROLLER_DESC::climbingMode
 		 */
 		 */
 		virtual void setClimbingMode(CharacterClimbingMode mode) = 0;
 		virtual void setClimbingMode(CharacterClimbingMode mode) = 0;
 
 
 		/** 
 		/** 
-		 * Returns non walkable mode that controls what happens when character encounters a slope higher than its slope 
-		 * offset. 
+		 * Returns non-walkable mode.
 		 *
 		 *
-		 * @see	CharacterNonWalkableMode
+		 * @copydoc CHAR_CONTROLLER_DESC::nonWalkableMode
 		 */
 		 */
 		virtual CharacterNonWalkableMode getNonWalkableMode() const = 0;
 		virtual CharacterNonWalkableMode getNonWalkableMode() const = 0;
 
 
 		/** 
 		/** 
-		 * Sets non walkable mode that controls what happens when character encounters a slope higher than its slope 
-		 * offset. 
+		 * Sets non-walkable mode.
 		 *
 		 *
-		 * @see	CharacterNonWalkableMode
+		 * @copydoc CHAR_CONTROLLER_DESC::nonWalkableMode
 		 */
 		 */
 		virtual void setNonWalkableMode(CharacterNonWalkableMode mode) = 0;
 		virtual void setNonWalkableMode(CharacterNonWalkableMode mode) = 0;
 
 
 		/** 
 		/** 
-		 * Returns minimum distance that the character will move during a call to move(). This is used to stop the recursive
-		 * motion algorithm when the remaining distance is too small.
+		 * Returns minimum move distance.
+		 *
+		 * @copydoc	CHAR_CONTROLLER_DESC::minMoveDistance
 		 */
 		 */
 		virtual float getMinMoveDistance() = 0;
 		virtual float getMinMoveDistance() = 0;
 
 
 		/** 
 		/** 
-		 * Sets minimum distance that the character will move during a call to move(). This is used to stop the recursive
-		 * motion algorithm when the remaining distance is too small.
+		 * Sets minimum move distance.
+		 *
+		 * @copydoc	CHAR_CONTROLLER_DESC::minMoveDistance
 		 */
 		 */
 		virtual void setMinMoveDistance(float value) = 0;
 		virtual void setMinMoveDistance(float value) = 0;
 
 
 		/** 
 		/** 
-		 * Returns the contact offset value. Contact offset specifies a skin around the object within which contacts will
-		 * be generated. It should be a small positive non-zero value.
+		 * Returns the contact offset.
+		 *
+		 * @copydoc	CHAR_CONTROLLER_DESC::contactOffset
 		 */
 		 */
 		virtual float getContactOffset() = 0;
 		virtual float getContactOffset() = 0;
 
 
-		/**
-		 * Sets the contact offset value. Contact offset specifies a skin around the object within which contacts will
-		 * be generated. It should be a small positive non-zero value.
+		/** 
+		 * Sets the contact offset.
+		 *
+		 * @copydoc	CHAR_CONTROLLER_DESC::contactOffset
 		 */
 		 */
 		virtual void setContactOffset(float value) = 0;
 		virtual void setContactOffset(float value) = 0;
 
 
 		/** 
 		/** 
-		 * Gets the step offset that controls which obstacles will the character be able to automatically step over without
-		 * being stopped. This is the height of the maximum obstacle that will be stepped over (with exceptions, see
-		 * setClimbingMode().
+		 * Returns the step offset.
+		 *
+		 * @copydoc	CHAR_CONTROLLER_DESC::stepOffset
 		 */
 		 */
 		virtual float getStepOffset() = 0;
 		virtual float getStepOffset() = 0;
 
 
 		/** 
 		/** 
-		 * Sets the step offset that controls which obstacles will the character be able to automatically step over without
-		 * being stopped. This is the height of the maximum obstacle that will be stepped over (with exceptions, see
-		 * setClimbingMode().
+		 * Sets the step offset.
+		 *
+		 * @copydoc	CHAR_CONTROLLER_DESC::stepOffset
 		 */
 		 */
 		virtual void setStepOffset(float value) = 0;
 		virtual void setStepOffset(float value) = 0;
 
 
 		/**
 		/**
-		 * Gets the slope angle that controls which slopes should the character consider too steep and won't be able to
-		 * move over. See setNonWalkableMode() for more information.
+		 * Returns the slope angle.
+		 *
+		 * @copydoc	CHAR_CONTROLLER_DESC::slopeLimit
 		 */
 		 */
-		virtual Radian getSlopeOffset() = 0;
+		virtual Radian getSlopeLimit() = 0;
 
 
 		/**
 		/**
-		 * Sets the slope angle that controls which slopes should the character consider too steep and won't be able to
-		 * move over. See setNonWalkableMode() for more information.
+		 * Sets the slope angle.
+		 *
+		 * @copydoc	CHAR_CONTROLLER_DESC::slopeLimit
 		 */
 		 */
-		virtual void setSlopeOffset(Radian value) = 0;
+		virtual void setSlopeLimit(Radian value) = 0;
+
+		/** Sets the layer that control swhat can the controller collide with. */
+		virtual void setLayer(UINT64 layer) { mLayer = layer; }
+
+		/** Gets the layer that control swhat can the controller collide with. */
+		virtual UINT64 getLayer() const { return mLayer; }
 
 
 		/** Creates a new character controller. */
 		/** Creates a new character controller. */
-		static SPtr<CharacterController> create();
+		static SPtr<CharacterController> create(const CHAR_CONTROLLER_DESC& desc);
+
+		/** Triggered when the controller hits a collider. */
+		Event<void(const ControllerColliderCollision&)> onColliderHit;
+
+		/** Triggered when the controller hits another character controller. */
+		Event<void(const ControllerControllerCollision&)> onControllerHit;
+
+	private:
+		UINT64 mLayer = 1;
+	};
+
+	/** Contains all the information required for initializing a character controller. */
+	struct CHAR_CONTROLLER_DESC
+	{
+		/** Center of the controller capsule */
+		Vector3 position;
+
+		/**
+		 * Contact offset specifies a skin around the object within which contacts will be generated. It should be a small
+		 * positive non-zero value.
+		 */
+		float contactOffset = 0.1f;
+
+		/**
+		 * Controls which obstacles will the character be able to automatically step over without being stopped. This is the
+		 * height of the maximum obstacle that will be stepped over (with exceptions, see climbingMode).
+		 */
+		float stepOffset = 0.5f;
+
+		/**
+		 * Controls which slopes should the character consider too steep and won't be able to move over. See
+		 * nonWalkableMode for more information.
+		 */
+		Radian slopeLimit = Degree(45.0f);
+
+		/** 
+		 * Represents minimum distance that the character will move during a call to move(). This is used to stop the
+		 * recursive motion algorithm when the remaining distance is too small
+		 */
+		float minMoveDistance = 0.0f;
+
+		/** Height between the centers of the two spheres of the controller capsule. */
+		float height = 0.0f; 
+
+		/** Radius of the controller capsule. */
+		float radius = 1.0f;
+		
+		/** Up direction of controller capsule. Determines capsule orientation. */
+		Vector3 up = Vector3::UNIT_Y;
+
+		/** 
+		 * Controls what happens when character encounters a height higher than its step offset. 
+		 *
+		 * @see	CharacterClimbingMode
+		 */
+		CharacterClimbingMode climbingMode = CharacterClimbingMode::Normal;
+
+		/** 
+		 * Controls what happens when character encounters a slope higher than its slope offset. 
+		 *
+		 * @see	CharacterNonWalkableMode
+		 */
+		CharacterNonWalkableMode nonWalkableMode = CharacterNonWalkableMode::Prevent;
+	};
+
+	/** Contains data about a collision of a character controller and another object. */
+	struct ControllerCollision
+	{
+		Vector3 position; /**< Contact position. */
+		Vector3 normal; /**< Contact normal. */
+		Vector3 motionDir; /**< Direction of motion after the hit. */
+		float motionAmount; /**< Magnitude of motion after the hit. */
+	};
+
+	/** Contains data about a collision of a character controller and a collider. */
+	struct ControllerColliderCollision : ControllerCollision
+	{
+		Collider* collider; /**< Collider that was touched. */
+		UINT32 triangleIndex; /**< Touched triangle index for mesh colliders. */
+	};
+
+	/** Contains data about a collision between two character controllers */
+	struct ControllerControllerCollision : ControllerCollision
+	{
+		CharacterController* controller; /**< Controller that was touched. */
 	};
 	};
 }
 }

+ 1 - 0
BansheeCore/Include/BsCorePrerequisites.h

@@ -293,6 +293,7 @@ namespace BansheeEngine
 	struct RENDER_TEXTURE_DESC;
 	struct RENDER_TEXTURE_DESC;
 	struct RENDER_WINDOW_DESC;
 	struct RENDER_WINDOW_DESC;
 	struct FONT_DESC;
 	struct FONT_DESC;
+	struct CHAR_CONTROLLER_DESC;
 
 
 	template<class T>
 	template<class T>
 	class CoreThreadAccessor;
 	class CoreThreadAccessor;

+ 1 - 1
BansheeCore/Include/BsPhysics.h

@@ -38,7 +38,7 @@ namespace BansheeEngine
 		virtual SPtr<D6Joint> createD6Joint() = 0;
 		virtual SPtr<D6Joint> createD6Joint() = 0;
 
 
 		/** @copydoc CharacterController::create */
 		/** @copydoc CharacterController::create */
-		virtual SPtr<CharacterController> createCharacterController() = 0;
+		virtual SPtr<CharacterController> createCharacterController(const CHAR_CONTROLLER_DESC& desc) = 0;
 
 
 		void toggleCollision(UINT64 groupA, UINT64 groupB, bool enabled);
 		void toggleCollision(UINT64 groupA, UINT64 groupB, bool enabled);
 		bool isCollisionEnabled(UINT64 groupA, UINT64 groupB) const;
 		bool isCollisionEnabled(UINT64 groupA, UINT64 groupB) const;

+ 2 - 2
BansheeCore/Source/BsCharacterController.cpp

@@ -5,8 +5,8 @@
 
 
 namespace BansheeEngine
 namespace BansheeEngine
 {
 {
-	SPtr<CharacterController> CharacterController::create()
+	SPtr<CharacterController> CharacterController::create(const CHAR_CONTROLLER_DESC& desc)
 	{
 	{
-		return gPhysics().createCharacterController();
+		return gPhysics().createCharacterController(desc);
 	}
 	}
 }
 }

+ 2 - 0
BansheePhysX/BansheePhysX.vcxproj

@@ -250,6 +250,7 @@
     <ClCompile Include="Source\BsPhysX.cpp" />
     <ClCompile Include="Source\BsPhysX.cpp" />
     <ClCompile Include="Source\BsPhysXBoxCollider.cpp" />
     <ClCompile Include="Source\BsPhysXBoxCollider.cpp" />
     <ClCompile Include="Source\BsPhysXCapsuleCollider.cpp" />
     <ClCompile Include="Source\BsPhysXCapsuleCollider.cpp" />
+    <ClCompile Include="Source\BsPhysXCharacterController.cpp" />
     <ClCompile Include="Source\BsPhysXD6Joint.cpp" />
     <ClCompile Include="Source\BsPhysXD6Joint.cpp" />
     <ClCompile Include="Source\BsPhysXDistanceJoint.cpp" />
     <ClCompile Include="Source\BsPhysXDistanceJoint.cpp" />
     <ClCompile Include="Source\BsPhysXFixedJoint.cpp" />
     <ClCompile Include="Source\BsPhysXFixedJoint.cpp" />
@@ -270,6 +271,7 @@
     <ClInclude Include="Include\BsPhysXBoxCollider.h" />
     <ClInclude Include="Include\BsPhysXBoxCollider.h" />
     <ClInclude Include="Include\BsFPhysXCollider.h" />
     <ClInclude Include="Include\BsFPhysXCollider.h" />
     <ClInclude Include="Include\BsPhysXCapsuleCollider.h" />
     <ClInclude Include="Include\BsPhysXCapsuleCollider.h" />
+    <ClInclude Include="Include\BsPhysXCharacterController.h" />
     <ClInclude Include="Include\BsPhysXD6Joint.h" />
     <ClInclude Include="Include\BsPhysXD6Joint.h" />
     <ClInclude Include="Include\BsPhysXDistanceJoint.h" />
     <ClInclude Include="Include\BsPhysXDistanceJoint.h" />
     <ClInclude Include="Include\BsPhysXFixedJoint.h" />
     <ClInclude Include="Include\BsPhysXFixedJoint.h" />

+ 6 - 0
BansheePhysX/BansheePhysX.vcxproj.filters

@@ -68,6 +68,9 @@
     <ClCompile Include="Source\BsPhysXD6Joint.cpp">
     <ClCompile Include="Source\BsPhysXD6Joint.cpp">
       <Filter>Source Files</Filter>
       <Filter>Source Files</Filter>
     </ClCompile>
     </ClCompile>
+    <ClCompile Include="Source\BsPhysXCharacterController.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <ClInclude Include="Include\BsPhysXPrerequisites.h">
     <ClInclude Include="Include\BsPhysXPrerequisites.h">
@@ -127,5 +130,8 @@
     <ClInclude Include="Include\BsPhysXD6Joint.h">
     <ClInclude Include="Include\BsPhysXD6Joint.h">
       <Filter>Header Files</Filter>
       <Filter>Header Files</Filter>
     </ClInclude>
     </ClInclude>
+    <ClInclude Include="Include\BsPhysXCharacterController.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   </ItemGroup>
 </Project>
 </Project>

+ 3 - 1
BansheePhysX/Include/BsPhysX.h

@@ -7,6 +7,7 @@
 #include "BSCollision.h"
 #include "BSCollision.h"
 #include "PxPhysics.h"
 #include "PxPhysics.h"
 #include "foundation/Px.h"
 #include "foundation/Px.h"
+#include "characterkinematic\PxControllerManager.h"
 #include "cooking/PxCooking.h"
 #include "cooking/PxCooking.h"
 
 
 namespace BansheeEngine
 namespace BansheeEngine
@@ -66,7 +67,7 @@ namespace BansheeEngine
 		SPtr<D6Joint> createD6Joint() override;
 		SPtr<D6Joint> createD6Joint() override;
 
 
 		/** @copydoc Physics::createCharacterController*/
 		/** @copydoc Physics::createCharacterController*/
-		SPtr<CharacterController> createCharacterController() override;
+		SPtr<CharacterController> createCharacterController(const CHAR_CONTROLLER_DESC& desc) override;
 
 
 		void _reportContactEvent(const ContactEvent& event);
 		void _reportContactEvent(const ContactEvent& event);
 		void _reportTriggerEvent(const TriggerEvent& event);
 		void _reportTriggerEvent(const TriggerEvent& event);
@@ -94,6 +95,7 @@ namespace BansheeEngine
 		physx::PxPhysics* mPhysics = nullptr;
 		physx::PxPhysics* mPhysics = nullptr;
 		physx::PxCooking* mCooking = nullptr;
 		physx::PxCooking* mCooking = nullptr;
 		physx::PxScene* mScene = nullptr;
 		physx::PxScene* mScene = nullptr;
+		physx::PxControllerManager* mCharManager = nullptr;
 
 
 		physx::PxMaterial* mDefaultMaterial = nullptr;
 		physx::PxMaterial* mDefaultMaterial = nullptr;
 		physx::PxTolerancesScale mScale;
 		physx::PxTolerancesScale mScale;

+ 116 - 0
BansheePhysX/Include/BsPhysXCharacterController.h

@@ -0,0 +1,116 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsPhysXPrerequisites.h"
+#include "BsCharacterController.h"
+#include "PxPhysics.h"
+#include "characterkinematic\PxCapsuleController.h"
+
+namespace BansheeEngine
+{
+	/** PhysX specific implementation if a CharacterController. */
+	class BS_PHYSX_EXPORT PhysXCharacterController : public CharacterController
+												   , physx::PxUserControllerHitReport
+		                                           , physx::PxQueryFilterCallback
+												   , physx::PxControllerFilterCallback
+	{
+	public:
+		PhysXCharacterController(physx::PxControllerManager* manager, const CHAR_CONTROLLER_DESC& desc);
+		~PhysXCharacterController();
+
+		/** @copydoc CharacterController::move */
+		CharacterCollisionFlags move(const Vector3& displacement) override;
+
+		/** @copydoc CharacterController::getPosition */
+		Vector3 getPosition() const override;
+
+		/** @copydoc CharacterController::setPosition */
+		void setPosition(const Vector3& position) override;
+
+		/** @copydoc CharacterController::getFootPosition */
+		Vector3 getFootPosition() const override;
+
+		/** @copydoc CharacterController::setFootPosition */
+		void setFootPosition(const Vector3& position) override;
+
+		/** @copydoc CharacterController::getRadius */
+		float getRadius() const override;
+
+		/** @copydoc CharacterController::setRadius */
+		void setRadius(float radius) override;
+
+		/** @copydoc CharacterController::getHeight */
+		float getHeight() const override;
+
+		/** @copydoc CharacterController::setHeight */
+		void setHeight(float height) override;
+
+		/** @copydoc CharacterController::getUp */
+		Vector3 getUp() const override;
+
+		/** @copydoc CharacterController::setUp */
+		void setUp(const Vector3& up) override;
+
+		/** @copydoc CharacterController::getClimbingMode */
+		CharacterClimbingMode getClimbingMode() const override;
+
+		/** @copydoc CharacterController::setClimbingMode */
+		void setClimbingMode(CharacterClimbingMode mode) override;
+
+		/** @copydoc CharacterController::getNonWalkableMode */
+		CharacterNonWalkableMode getNonWalkableMode() const override;
+
+		/** @copydoc CharacterController::setNonWalkableMode */
+		void setNonWalkableMode(CharacterNonWalkableMode mode) override;
+
+		/** @copydoc CharacterController::getMinMoveDistance */
+		float getMinMoveDistance() override;
+
+		/** @copydoc CharacterController::setMinMoveDistance */
+		void setMinMoveDistance(float value) override;
+
+		/** @copydoc CharacterController::getContactOffset */
+		float getContactOffset() override;
+
+		/** @copydoc CharacterController::setContactOffset */
+		void setContactOffset(float value) override;
+
+		/** @copydoc CharacterController::getStepOffset */
+		float getStepOffset() override;
+
+		/** @copydoc CharacterController::setStepOffset */
+		void setStepOffset(float value) override;
+
+		/** @copydoc CharacterController::getSlopeLimit */
+		Radian getSlopeLimit() override;
+
+		/** @copydoc CharacterController::setSlopeLimit */
+		void setSlopeLimit(Radian value) override;
+
+	private:
+		/** @copydoc physx::PxUserControllerHitReport::onShapeHit */
+		void onShapeHit(const physx::PxControllerShapeHit& hit) override;
+
+		/** @copydoc physx::PxUserControllerHitReport::onControllerHit */
+		void onControllerHit(const physx::PxControllersHit& hit) override;
+
+		/** @copydoc physx::PxUserControllerHitReport::onObstacleHit */
+		void onObstacleHit(const physx::PxControllerObstacleHit& hit) override { /* Do nothing */ };
+
+		/** @copydoc physx::PxQueryFilterCallback::preFilter */
+		physx::PxQueryHitType::Enum preFilter(const physx::PxFilterData& filterData, const physx::PxShape* shape, 
+			const physx::PxRigidActor* actor, physx::PxHitFlags& queryFlags) override;
+
+		/** @copydoc physx::PxQueryFilterCallback::postFilter */
+		physx::PxQueryHitType::Enum postFilter(const physx::PxFilterData& filterData, 
+			const physx::PxQueryHit& hit) override;
+
+		/** @copydoc physx::PxControllerFilterCallback::filter */
+		bool filter(const physx::PxController& a, const physx::PxController& b) override;
+
+		physx::PxCapsuleController* mController = nullptr;
+		float mMinMoveDistance = 0.0f;
+		float mLastMoveCall = 0.0f;
+	};
+}

+ 8 - 3
BansheePhysX/Source/BsPhysX.cpp

@@ -14,6 +14,7 @@
 #include "BsPhysXSphericalJoint.h"
 #include "BsPhysXSphericalJoint.h"
 #include "BsPhysXSliderJoint.h"
 #include "BsPhysXSliderJoint.h"
 #include "BsPhysXD6Joint.h"
 #include "BsPhysXD6Joint.h"
+#include "BsPhysXCharacterController.h"
 #include "BsTaskScheduler.h"
 #include "BsTaskScheduler.h"
 #include "BsTime.h"
 #include "BsTime.h"
 #include "Bsvector3.h"
 #include "Bsvector3.h"
@@ -329,12 +330,17 @@ namespace BansheeEngine
 		sceneDesc.flags = PxSceneFlag::eENABLE_ACTIVETRANSFORMS;
 		sceneDesc.flags = PxSceneFlag::eENABLE_ACTIVETRANSFORMS;
 
 
 		mScene = mPhysics->createScene(sceneDesc);
 		mScene = mPhysics->createScene(sceneDesc);
+
+		// Character controller
+		mCharManager = PxCreateControllerManager(*mScene);
+
 		mSimulationStep = input.timeStep;
 		mSimulationStep = input.timeStep;
 		mDefaultMaterial = mPhysics->createMaterial(0.0f, 0.0f, 0.0f);
 		mDefaultMaterial = mPhysics->createMaterial(0.0f, 0.0f, 0.0f);
 	}
 	}
 
 
 	PhysX::~PhysX()
 	PhysX::~PhysX()
 	{
 	{
+		mCharManager->release();
 		mScene->release();
 		mScene->release();
 
 
 		if (mCooking != nullptr)
 		if (mCooking != nullptr)
@@ -532,10 +538,9 @@ namespace BansheeEngine
 		return bs_shared_ptr_new<PhysXMeshCollider>(mPhysics, position, rotation);
 		return bs_shared_ptr_new<PhysXMeshCollider>(mPhysics, position, rotation);
 	}
 	}
 
 
-	SPtr<CharacterController> PhysX::createCharacterController()
+	SPtr<CharacterController> PhysX::createCharacterController(const CHAR_CONTROLLER_DESC& desc)
 	{
 	{
-		// TODO - Not implemented
-		return nullptr;
+		return bs_shared_ptr_new<PhysXCharacterController>(mCharManager, desc);
 	}
 	}
 
 
 	SPtr<FixedJoint> PhysX::createFixedJoint()
 	SPtr<FixedJoint> PhysX::createFixedJoint()

+ 276 - 0
BansheePhysX/Source/BsPhysXCharacterController.cpp

@@ -0,0 +1,276 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsPhysXCharacterController.h"
+#include "BsTime.h"
+#include "BsPhysX.h"
+#include "characterkinematic\PxControllerManager.h"
+
+using namespace physx;
+
+namespace BansheeEngine
+{
+	PxExtendedVec3 toPxExtVector(const Vector3& input)
+	{
+		return PxExtendedVec3(input.x, input.y, input.z);
+	}
+
+	Vector3 fromPxExtVector(const PxExtendedVec3& input)
+	{
+		return Vector3((float)input.x, (float)input.y, (float)input.z);
+	}
+
+	PxCapsuleClimbingMode::Enum toPxEnum(CharacterClimbingMode value)
+	{
+		return value == CharacterClimbingMode::Normal
+			? PxCapsuleClimbingMode::eEASY
+			: PxCapsuleClimbingMode::eCONSTRAINED;
+	}
+
+	PxControllerNonWalkableMode::Enum toPxEnum(CharacterNonWalkableMode value)
+	{
+		return value == CharacterNonWalkableMode::Prevent
+			? PxControllerNonWalkableMode::ePREVENT_CLIMBING
+			: PxControllerNonWalkableMode::ePREVENT_CLIMBING_AND_FORCE_SLIDING;
+	}
+
+	CharacterClimbingMode fromPxEnum(PxCapsuleClimbingMode::Enum value)
+	{
+		return value == PxCapsuleClimbingMode::eEASY
+			? CharacterClimbingMode::Normal
+			: CharacterClimbingMode::Constrained;
+	}
+
+	CharacterNonWalkableMode fromPxEnum(PxControllerNonWalkableMode::Enum value)
+	{
+		return value == PxControllerNonWalkableMode::ePREVENT_CLIMBING
+			? CharacterNonWalkableMode::Prevent
+			: CharacterNonWalkableMode::PreventAndSlide;
+	}
+
+	PxCapsuleControllerDesc toPxDesc(const CHAR_CONTROLLER_DESC& desc)
+	{
+		PxCapsuleControllerDesc output;
+		output.climbingMode = toPxEnum(desc.climbingMode);
+		output.nonWalkableMode = toPxEnum(desc.nonWalkableMode);
+		output.contactOffset = desc.contactOffset;
+		output.stepOffset = desc.stepOffset;
+		output.slopeLimit = desc.slopeLimit.valueRadians();
+		output.height = desc.height;
+		output.radius = desc.radius;
+		output.upDirection = toPxVector(desc.up);
+		output.position = toPxExtVector(desc.position);
+
+		return output;
+	}
+
+	PhysXCharacterController::PhysXCharacterController(PxControllerManager* manager, const CHAR_CONTROLLER_DESC& desc)
+		:CharacterController(desc)
+	{
+		PxCapsuleControllerDesc pxDesc = toPxDesc(desc);
+		pxDesc.reportCallback = this;
+
+		mController = static_cast<PxCapsuleController*>(manager->createController(pxDesc));
+		mController->setUserData(this);
+	}
+
+	PhysXCharacterController::~PhysXCharacterController()
+	{
+		mController->release();
+	}
+
+	CharacterCollisionFlags PhysXCharacterController::move(const Vector3& displacement)
+	{
+		PxControllerFilters filters;
+		filters.mFilterCallback = this;
+		filters.mFilterFlags = PxQueryFlag::eANY_HIT | PxQueryFlag::eSTATIC | PxQueryFlag::eDYNAMIC | PxQueryFlag::ePREFILTER;
+		filters.mCCTFilterCallback = this;
+
+		float curTime = gTime().getTime();
+		float delta = curTime - mLastMoveCall;
+		mLastMoveCall = curTime;
+
+		PxControllerCollisionFlags collisionFlag = mController->move(toPxVector(displacement), mMinMoveDistance, delta, filters);
+
+		CharacterCollisionFlags output;
+		if (collisionFlag.isSet(PxControllerCollisionFlag::eCOLLISION_DOWN))
+			output.set(CharacterCollisionFlag::Down);
+
+		if (collisionFlag.isSet(PxControllerCollisionFlag::eCOLLISION_UP))
+			output.set(CharacterCollisionFlag::Up);
+
+		if (collisionFlag.isSet(PxControllerCollisionFlag::eCOLLISION_SIDES))
+			output.set(CharacterCollisionFlag::Sides);
+
+		return output;
+	}
+
+	Vector3 PhysXCharacterController::getPosition() const
+	{
+		return fromPxExtVector(mController->getPosition());
+	}
+
+	void PhysXCharacterController::setPosition(const Vector3& position)
+	{
+		mController->setPosition(toPxExtVector(position));
+	}
+
+	Vector3 PhysXCharacterController::getFootPosition() const
+	{
+		return fromPxExtVector(mController->getFootPosition());
+	}
+
+	void PhysXCharacterController::setFootPosition(const Vector3& position)
+	{
+		mController->setFootPosition(toPxExtVector(position));
+	}
+
+	float PhysXCharacterController::getRadius() const
+	{
+		return mController->getRadius();
+	}
+
+	void PhysXCharacterController::setRadius(float radius)
+	{
+		mController->setRadius(radius);
+	}
+
+	float PhysXCharacterController::getHeight() const
+	{
+		return mController->getHeight();
+	}
+
+	void PhysXCharacterController::setHeight(float height)
+	{
+		mController->setHeight(height);
+	}
+
+	Vector3 PhysXCharacterController::getUp() const
+	{
+		return fromPxVector(mController->getUpDirection());
+	}
+
+	void PhysXCharacterController::setUp(const Vector3& up)
+	{
+		mController->setUpDirection(toPxVector(up));
+	}
+
+	CharacterClimbingMode PhysXCharacterController::getClimbingMode() const
+	{
+		return fromPxEnum(mController->getClimbingMode());
+	}
+
+	void PhysXCharacterController::setClimbingMode(CharacterClimbingMode mode)
+	{
+		mController->setClimbingMode(toPxEnum(mode));
+	}
+
+	CharacterNonWalkableMode PhysXCharacterController::getNonWalkableMode() const
+	{
+		return fromPxEnum(mController->getNonWalkableMode());
+	}
+
+	void PhysXCharacterController::setNonWalkableMode(CharacterNonWalkableMode mode)
+	{
+		mController->setNonWalkableMode(toPxEnum(mode));
+	}
+
+	float PhysXCharacterController::getMinMoveDistance()
+	{
+		return mMinMoveDistance;
+	}
+
+	void PhysXCharacterController::setMinMoveDistance(float value)
+	{
+		mMinMoveDistance = value;
+	}
+
+	float PhysXCharacterController::getContactOffset()
+	{
+		return mController->getContactOffset();
+	}
+
+	void PhysXCharacterController::setContactOffset(float value)
+	{
+		mController->setContactOffset(value);
+	}
+
+	float PhysXCharacterController::getStepOffset()
+	{
+		return mController->getStepOffset();
+	}
+
+	void PhysXCharacterController::setStepOffset(float value)
+	{
+		mController->setStepOffset(value);
+	}
+
+	Radian PhysXCharacterController::getSlopeLimit()
+	{
+		return Radian(mController->getSlopeLimit());
+	}
+
+	void PhysXCharacterController::setSlopeLimit(Radian value)
+	{
+		mController->setSlopeLimit(value.valueRadians());
+	}
+
+	void PhysXCharacterController::onShapeHit(const PxControllerShapeHit& hit)
+	{
+		if (onColliderHit.empty())
+			return;
+
+		ControllerColliderCollision collision;
+		collision.position = fromPxExtVector(hit.worldPos);
+		collision.normal = fromPxVector(hit.worldNormal);
+		collision.motionDir = fromPxVector(hit.dir);
+		collision.motionAmount = hit.length;
+		collision.triangleIndex = hit.triangleIndex;
+		collision.collider = (Collider*)hit.shape->userData;
+
+		onColliderHit(collision);
+	}
+
+	void PhysXCharacterController::onControllerHit(const PxControllersHit& hit)
+	{
+		if (CharacterController::onControllerHit.empty())
+			return;
+
+		ControllerControllerCollision collision;
+		collision.position = fromPxExtVector(hit.worldPos);
+		collision.normal = fromPxVector(hit.worldNormal);
+		collision.motionDir = fromPxVector(hit.dir);
+		collision.motionAmount = hit.length;
+		collision.controller = (CharacterController*)hit.controller->getUserData();
+
+		CharacterController::onControllerHit(collision);
+	}
+
+	PxQueryHitType::Enum PhysXCharacterController::preFilter(const PxFilterData& filterData, const PxShape* shape,
+		const PxRigidActor* actor, PxHitFlags& queryFlags)
+	{
+		PxFilterData colliderFilterData = shape->getSimulationFilterData();
+		UINT64 colliderLayer = *(UINT64*)&colliderFilterData.word0;
+
+		bool canCollide = gPhysics().isCollisionEnabled(colliderLayer, getLayer());
+
+		if(canCollide)
+			return PxSceneQueryHitType::eBLOCK;
+
+		return PxSceneQueryHitType::eNONE;
+	}
+
+	PxQueryHitType::Enum PhysXCharacterController::postFilter(const PxFilterData& filterData,
+		const PxQueryHit& hit)
+	{
+		return PxSceneQueryHitType::eBLOCK;
+	}
+
+	bool PhysXCharacterController::filter(const PxController& a, const PxController& b)
+	{
+		CharacterController* controllerA = (CharacterController*)a.getUserData();
+		CharacterController* controllerB = (CharacterController*)b.getUserData();
+
+		bool canCollide = gPhysics().isCollisionEnabled(controllerA->getLayer(), controllerB->getLayer());
+		return canCollide;
+	}
+}