1//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
2//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
3#include "BsPhysXCharacterController.h"
4#include "Utility/BsTime.h"
5#include "BsPhysX.h"
6#include "Components/BsCCollider.h"
7#include "characterkinematic/PxControllerManager.h"
8
9using namespace physx;
10
11namespace bs
12{
13 PxExtendedVec3 toPxExtVector(const Vector3& input)
14 {
15 return PxExtendedVec3(input.x, input.y, input.z);
16 }
17
18 Vector3 fromPxExtVector(const PxExtendedVec3& input)
19 {
20 return Vector3((float)input.x, (float)input.y, (float)input.z);
21 }
22
23 PxCapsuleClimbingMode::Enum toPxEnum(CharacterClimbingMode value)
24 {
25 return value == CharacterClimbingMode::Normal
26 ? PxCapsuleClimbingMode::eEASY
27 : PxCapsuleClimbingMode::eCONSTRAINED;
28 }
29
30 PxControllerNonWalkableMode::Enum toPxEnum(CharacterNonWalkableMode value)
31 {
32 return value == CharacterNonWalkableMode::Prevent
33 ? PxControllerNonWalkableMode::ePREVENT_CLIMBING
34 : PxControllerNonWalkableMode::ePREVENT_CLIMBING_AND_FORCE_SLIDING;
35 }
36
37 CharacterClimbingMode fromPxEnum(PxCapsuleClimbingMode::Enum value)
38 {
39 return value == PxCapsuleClimbingMode::eEASY
40 ? CharacterClimbingMode::Normal
41 : CharacterClimbingMode::Constrained;
42 }
43
44 CharacterNonWalkableMode fromPxEnum(PxControllerNonWalkableMode::Enum value)
45 {
46 return value == PxControllerNonWalkableMode::ePREVENT_CLIMBING
47 ? CharacterNonWalkableMode::Prevent
48 : CharacterNonWalkableMode::PreventAndSlide;
49 }
50
51 PxCapsuleControllerDesc toPxDesc(const CHAR_CONTROLLER_DESC& desc)
52 {
53 PxCapsuleControllerDesc output;
54 output.climbingMode = toPxEnum(desc.climbingMode);
55 output.nonWalkableMode = toPxEnum(desc.nonWalkableMode);
56 output.contactOffset = desc.contactOffset;
57 output.stepOffset = desc.stepOffset;
58 output.slopeLimit = desc.slopeLimit.valueRadians();
59 output.height = desc.height;
60 output.radius = desc.radius;
61 output.upDirection = toPxVector(desc.up);
62 output.position = toPxExtVector(desc.position);
63
64 return output;
65 }
66
67 PhysXCharacterController::PhysXCharacterController(PxControllerManager* manager, const CHAR_CONTROLLER_DESC& desc)
68 :CharacterController(desc)
69 {
70 PxCapsuleControllerDesc pxDesc = toPxDesc(desc);
71 pxDesc.reportCallback = this;
72 pxDesc.material = gPhysX().getDefaultMaterial();
73 pxDesc.height = pxDesc.height <= 0 ? 0.01f : pxDesc.height;
74
75 mController = static_cast<PxCapsuleController*>(manager->createController(pxDesc));
76 mController->setUserData(this);
77 }
78
79 PhysXCharacterController::~PhysXCharacterController()
80 {
81 mController->setUserData(nullptr);
82 mController->release();
83 }
84
85 CharacterCollisionFlags PhysXCharacterController::move(const Vector3& displacement)
86 {
87 PxControllerFilters filters;
88 filters.mFilterCallback = this;
89 filters.mFilterFlags = PxQueryFlag::eANY_HIT | PxQueryFlag::eSTATIC | PxQueryFlag::eDYNAMIC | PxQueryFlag::ePREFILTER;
90 filters.mCCTFilterCallback = this;
91
92 float curTime = gTime().getTime();
93 float delta = curTime - mLastMoveCall;
94 mLastMoveCall = curTime;
95
96 PxControllerCollisionFlags collisionFlag = mController->move(toPxVector(displacement), mMinMoveDistance, delta, filters);
97
98 CharacterCollisionFlags output;
99 if (collisionFlag.isSet(PxControllerCollisionFlag::eCOLLISION_DOWN))
100 output.set(CharacterCollisionFlag::Down);
101
102 if (collisionFlag.isSet(PxControllerCollisionFlag::eCOLLISION_UP))
103 output.set(CharacterCollisionFlag::Up);
104
105 if (collisionFlag.isSet(PxControllerCollisionFlag::eCOLLISION_SIDES))
106 output.set(CharacterCollisionFlag::Sides);
107
108 return output;
109 }
110
111 Vector3 PhysXCharacterController::getPosition() const
112 {
113 return fromPxExtVector(mController->getPosition());
114 }
115
116 void PhysXCharacterController::setPosition(const Vector3& position)
117 {
118 mController->setPosition(toPxExtVector(position));
119 }
120
121 Vector3 PhysXCharacterController::getFootPosition() const
122 {
123 return fromPxExtVector(mController->getFootPosition());
124 }
125
126 void PhysXCharacterController::setFootPosition(const Vector3& position)
127 {
128 mController->setFootPosition(toPxExtVector(position));
129 }
130
131 float PhysXCharacterController::getRadius() const
132 {
133 return mController->getRadius();
134 }
135
136 void PhysXCharacterController::setRadius(float radius)
137 {
138 mController->setRadius(radius);
139 }
140
141 float PhysXCharacterController::getHeight() const
142 {
143 return mController->getHeight();
144 }
145
146 void PhysXCharacterController::setHeight(float height)
147 {
148 mController->setHeight(height);
149 }
150
151 Vector3 PhysXCharacterController::getUp() const
152 {
153 return fromPxVector(mController->getUpDirection());
154 }
155
156 void PhysXCharacterController::setUp(const Vector3& up)
157 {
158 mController->setUpDirection(toPxVector(up));
159 }
160
161 CharacterClimbingMode PhysXCharacterController::getClimbingMode() const
162 {
163 return fromPxEnum(mController->getClimbingMode());
164 }
165
166 void PhysXCharacterController::setClimbingMode(CharacterClimbingMode mode)
167 {
168 mController->setClimbingMode(toPxEnum(mode));
169 }
170
171 CharacterNonWalkableMode PhysXCharacterController::getNonWalkableMode() const
172 {
173 return fromPxEnum(mController->getNonWalkableMode());
174 }
175
176 void PhysXCharacterController::setNonWalkableMode(CharacterNonWalkableMode mode)
177 {
178 mController->setNonWalkableMode(toPxEnum(mode));
179 }
180
181 float PhysXCharacterController::getMinMoveDistance() const
182 {
183 return mMinMoveDistance;
184 }
185
186 void PhysXCharacterController::setMinMoveDistance(float value)
187 {
188 mMinMoveDistance = value;
189 }
190
191 float PhysXCharacterController::getContactOffset() const
192 {
193 return mController->getContactOffset();
194 }
195
196 void PhysXCharacterController::setContactOffset(float value)
197 {
198 mController->setContactOffset(value);
199 }
200
201 float PhysXCharacterController::getStepOffset() const
202 {
203 return mController->getStepOffset();
204 }
205
206 void PhysXCharacterController::setStepOffset(float value)
207 {
208 mController->setStepOffset(value);
209 }
210
211 Radian PhysXCharacterController::getSlopeLimit() const
212 {
213 return Radian(mController->getSlopeLimit());
214 }
215
216 void PhysXCharacterController::setSlopeLimit(Radian value)
217 {
218 mController->setSlopeLimit(value.valueRadians());
219 }
220
221 void PhysXCharacterController::onShapeHit(const PxControllerShapeHit& hit)
222 {
223 if (onColliderHit.empty())
224 return;
225
226 ControllerColliderCollision collision;
227 collision.position = fromPxExtVector(hit.worldPos);
228 collision.normal = fromPxVector(hit.worldNormal);
229 collision.motionDir = fromPxVector(hit.dir);
230 collision.motionAmount = hit.length;
231 collision.triangleIndex = hit.triangleIndex;
232 collision.colliderRaw = (Collider*)hit.shape->userData;
233
234 onColliderHit(collision);
235 }
236
237 void PhysXCharacterController::onControllerHit(const PxControllersHit& hit)
238 {
239 if (CharacterController::onControllerHit.empty())
240 return;
241
242 ControllerControllerCollision collision;
243 collision.position = fromPxExtVector(hit.worldPos);
244 collision.normal = fromPxVector(hit.worldNormal);
245 collision.motionDir = fromPxVector(hit.dir);
246 collision.motionAmount = hit.length;
247 collision.controllerRaw = (CharacterController*)hit.controller->getUserData();
248
249 CharacterController::onControllerHit(collision);
250 }
251
252 PxQueryHitType::Enum PhysXCharacterController::preFilter(const PxFilterData& filterData, const PxShape* shape,
253 const PxRigidActor* actor, PxHitFlags& queryFlags)
254 {
255 PxFilterData colliderFilterData = shape->getSimulationFilterData();
256 UINT64 colliderLayer = *(UINT64*)&colliderFilterData.word0;
257
258 bool canCollide = gPhysics().isCollisionEnabled(colliderLayer, getLayer());
259
260 if(canCollide)
261 return PxSceneQueryHitType::eBLOCK;
262
263 return PxSceneQueryHitType::eNONE;
264 }
265
266 PxQueryHitType::Enum PhysXCharacterController::postFilter(const PxFilterData& filterData,
267 const PxQueryHit& hit)
268 {
269 return PxSceneQueryHitType::eBLOCK;
270 }
271
272 bool PhysXCharacterController::filter(const PxController& a, const PxController& b)
273 {
274 CharacterController* controllerA = (CharacterController*)a.getUserData();
275 CharacterController* controllerB = (CharacterController*)b.getUserData();
276
277 bool canCollide = gPhysics().isCollisionEnabled(controllerA->getLayer(), controllerB->getLayer());
278 return canCollide;
279 }
280}