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