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 | |
9 | using namespace physx; |
10 | |
11 | namespace 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::() const |
122 | { |
123 | return fromPxExtVector(mController->getFootPosition()); |
124 | } |
125 | |
126 | void PhysXCharacterController::(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 | } |