| 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 | } |