MyAvatar now owns its CharacterController fix for bug of phantom collision obj when avatar's physics are disabled
Showing the changes to libraries/physics/src/CharacterController.cpp (1 of 8 files). Show all
Unified · Split
| @@ -21,6 +21,12 @@ subject to the following restrictions: · 20 unmodified lines | |||
| 21 | #include "BulletUtil.h" | 21 | #include "BulletUtil.h" |
| 22 | #include "CharacterController.h" | 22 | #include "CharacterController.h" |
| 23 | 23 | ||
| 24 | const uint32_t PENDING_FLAG_ADD_TO_SIMULATION = 1U << 0; | ||
| 25 | const uint32_t PENDING_FLAG_REMOVE_FROM_SIMULATION = 1U << 1; | ||
| 26 | //const uint32_t PENDING_FLAG_ENABLE = 1U << 0; | ||
| 27 | //const uint32_t PENDING_FLAG_DISABLE = 1U << 1; | ||
| 28 | const uint32_t PENDING_FLAG_UPDATE_SHAPE = 1U << 2; | ||
| 29 | const uint32_t PENDING_FLAG_JUMP = 1U << 4; | ||
| 24 | 30 | ||
| 25 | // static helper method | 31 | // static helper method |
| 26 | static btVector3 getNormalizedVector(const btVector3& v) { | 32 | static btVector3 getNormalizedVector(const btVector3& v) { |
| @@ -223,11 +229,10 @@ CharacterController::CharacterController(AvatarData* avatarData) { · 196 unmodified lines | |||
| 223 | _avatarData = avatarData; | 229 | _avatarData = avatarData; |
| 224 | 230 | ||
| 225 | // cache the "PhysicsEnabled" state of _avatarData | 231 | // cache the "PhysicsEnabled" state of _avatarData |
| 226 | _avatarData->lockForRead(); | 232 | _enabled = false; |
| 227 | _enabled = _avatarData->isPhysicsEnabled(); | ||
| 228 | _avatarData->unlock(); | ||
| 229 | 233 | ||
| 230 | createShapeAndGhost(); | 234 | _ghostObject = NULL; |
| 235 | _convexShape = NULL; | ||
| 231 | 236 | ||
| 232 | _addedMargin = 0.02f; | 237 | _addedMargin = 0.02f; |
| 233 | _walkDirection.setValue(0.0f,0.0f,0.0f); | 238 | _walkDirection.setValue(0.0f,0.0f,0.0f); |
| @@ -242,6 +247,7 @@ CharacterController::CharacterController(AvatarData* avatarData) { · 8 unmodified lines | |||
| 242 | _wasJumping = false; | 247 | _wasJumping = false; |
| 243 | setMaxSlope(btRadians(45.0f)); | 248 | setMaxSlope(btRadians(45.0f)); |
| 244 | _lastStepUp = 0.0f; | 249 | _lastStepUp = 0.0f; |
| 250 | _pendingFlags = 0; | ||
| 245 | } | 251 | } |
| 246 | 252 | ||
| 247 | CharacterController::~CharacterController() { | 253 | CharacterController::~CharacterController() { |
| @@ -349,7 +355,7 @@ bool CharacterController::recoverFromPenetration(btCollisionWorld* collisionWorl · 101 unmodified lines | |||
| 349 | return penetration; | 355 | return penetration; |
| 350 | } | 356 | } |
| 351 | 357 | ||
| 352 | void CharacterController::stepUp( btCollisionWorld* world) { | 358 | void CharacterController::stepUp(btCollisionWorld* world) { |
| 353 | // phase 1: up | 359 | // phase 1: up |
| 354 | 360 | ||
| 355 | // compute start and end | 361 | // compute start and end |
| @@ -416,7 +422,7 @@ void CharacterController::updateTargetPositionBasedOnCollision(const btVector3& · 60 unmodified lines | |||
| 416 | } | 422 | } |
| 417 | } | 423 | } |
| 418 | 424 | ||
| 419 | void CharacterController::stepForward( btCollisionWorld* collisionWorld, const btVector3& movement) { | 425 | void CharacterController::stepForward(btCollisionWorld* collisionWorld, const btVector3& movement) { |
| 420 | // phase 2: forward | 426 | // phase 2: forward |
| 421 | _targetPosition = _currentPosition + movement; | 427 | _targetPosition = _currentPosition + movement; |
| 422 | 428 | ||
| @@ -472,7 +478,7 @@ void CharacterController::stepForward( btCollisionWorld* collisionWorld, const b · 49 unmodified lines | |||
| 472 | _convexShape->setMargin(margin); | 478 | _convexShape->setMargin(margin); |
| 473 | } | 479 | } |
| 474 | 480 | ||
| 475 | void CharacterController::stepDown( btCollisionWorld* collisionWorld, btScalar dt) { | 481 | void CharacterController::stepDown(btCollisionWorld* collisionWorld, btScalar dt) { |
| 476 | // phase 3: down | 482 | // phase 3: down |
| 477 | // | 483 | // |
| 478 | // The "stepDown" phase first makes a normal sweep down that cancels the lift from the "stepUp" phase. | 484 | // The "stepDown" phase first makes a normal sweep down that cancels the lift from the "stepUp" phase. |
| @@ -558,7 +564,7 @@ void CharacterController::setVelocityForTimeInterval(const btVector3& velocity, · 79 unmodified lines | |||
| 558 | _velocityTimeInterval += timeInterval; | 564 | _velocityTimeInterval += timeInterval; |
| 559 | } | 565 | } |
| 560 | 566 | ||
| 561 | void CharacterController::reset( btCollisionWorld* collisionWorld ) { | 567 | void CharacterController::reset(btCollisionWorld* collisionWorld) { |
| 562 | _verticalVelocity = 0.0; | 568 | _verticalVelocity = 0.0; |
| 563 | _verticalOffset = 0.0; | 569 | _verticalOffset = 0.0; |
| 564 | _wasOnGround = false; | 570 | _wasOnGround = false; |
| @@ -583,7 +589,7 @@ void CharacterController::warp(const btVector3& origin) { · 18 unmodified lines | |||
| 583 | } | 589 | } |
| 584 | 590 | ||
| 585 | 591 | ||
| 586 | void CharacterController::preStep( btCollisionWorld* collisionWorld) { | 592 | void CharacterController::preStep(btCollisionWorld* collisionWorld) { |
| 587 | if (!_enabled) { | 593 | if (!_enabled) { |
| 588 | return; | 594 | return; |
| 589 | } | 595 | } |
| @@ -603,7 +609,7 @@ void CharacterController::preStep( btCollisionWorld* collisionWorld) { · 13 unmodified lines | |||
| 603 | _targetPosition = _currentPosition; | 609 | _targetPosition = _currentPosition; |
| 604 | } | 610 | } |
| 605 | 611 | ||
| 606 | void CharacterController::playerStep( btCollisionWorld* collisionWorld, btScalar dt) { | 612 | void CharacterController::playerStep(btCollisionWorld* collisionWorld, btScalar dt) { |
| 607 | if (!_enabled || (!_useWalkDirection && _velocityTimeInterval <= 0.0)) { | 613 | if (!_enabled || (!_useWalkDirection && _velocityTimeInterval <= 0.0)) { |
| 608 | return; // no motion | 614 | return; // no motion |
| 609 | } | 615 | } |
| @@ -710,106 +716,163 @@ void CharacterController::setUpInterpolate(bool value) { · 100 unmodified lines | |||
| 710 | // (interpolate = true, and now default behavior) or happily penetrate objects above the avatar. | 716 | // (interpolate = true, and now default behavior) or happily penetrate objects above the avatar. |
| 711 | } | 717 | } |
| 712 | 718 | ||
| 719 | /* | ||
| 713 | // protected | 720 | // protected |
| 714 | void CharacterController::createShapeAndGhost() { | 721 | void CharacterController::createShapeAndGhost() { |
| 715 | // get new dimensions from avatar | 722 | // get new dimensions from avatar |
| 716 | _avatarData->lockForRead(); | 723 | float x = _boxScale.x; |
| 717 | AABox box = _avatarData->getLocalAABox(); | 724 | float z = _boxScale.z; |
| 718 | 725 | _radius = 0.5f * sqrtf(0.5f * (x * x + z * z)); | |
| 719 | // create new ghost | 726 | _halfHeight = 0.5f * _boxScale.y - _radius; |
| 720 | _ghostObject = new btPairCachingGhostObject(); | ||
| 721 | _ghostObject->setWorldTransform(btTransform(glmToBullet(_avatarData->getOrientation()), | ||
| 722 | glmToBullet(_avatarData->getPosition()))); | ||
| 723 | _avatarData->unlock(); | ||
| 724 | |||
| 725 | const glm::vec3& diagonal = box.getScale(); | ||
| 726 | _radius = 0.5f * sqrtf(0.5f * (diagonal.x * diagonal.x + diagonal.z * diagonal.z)); | ||
| 727 | _halfHeight = 0.5f * diagonal.y - _radius; | ||
| 728 | float MIN_HALF_HEIGHT = 0.1f; | 727 | float MIN_HALF_HEIGHT = 0.1f; |
| 729 | if (_halfHeight < MIN_HALF_HEIGHT) { | 728 | if (_halfHeight < MIN_HALF_HEIGHT) { |
| 730 | _halfHeight = MIN_HALF_HEIGHT; | 729 | _halfHeight = MIN_HALF_HEIGHT; |
| 731 | } | 730 | } |
| 732 | glm::vec3 offset = box.getCorner() + 0.5f * diagonal; | 731 | // NOTE: _shapeLocalOffset is already computed |
| 733 | _shapeLocalOffset = offset; | 732 | |
| 734 | 733 | if (_radius > 0.0f) { | |
| 735 | // stepHeight affects the heights of ledges that the character can ascend | 734 | // create new ghost |
| 736 | // however the actual ledge height is some function of _stepHeight | 735 | _ghostObject = new btPairCachingGhostObject(); |
| 737 | // due to character shape and this CharacterController algorithm | 736 | _ghostObject->setWorldTransform(btTransform(glmToBullet(_avatarData->getOrientation()), |
| 738 | // (the function is approximately 2*_stepHeight) | 737 | glmToBullet(_avatarData->getPosition()))); |
| 739 | _stepHeight = 0.1f * (_radius + _halfHeight) + 0.1f; | 738 | // stepHeight affects the heights of ledges that the character can ascend |
| 740 | 739 | // however the actual ledge height is some function of _stepHeight | |
| 741 | // create new shape | 740 | // due to character shape and this CharacterController algorithm |
| 742 | _convexShape = new btCapsuleShape(_radius, 2.0f * _halfHeight); | 741 | // (the function is approximately 2*_stepHeight) |
| 743 | _ghostObject->setCollisionShape(_convexShape); | 742 | _stepHeight = 0.1f * (_radius + _halfHeight) + 0.1f; |
| 744 | _ghostObject->setCollisionFlags(btCollisionObject::CF_CHARACTER_OBJECT); | 743 | |
| 744 | // create new shape | ||
| 745 | _convexShape = new btCapsuleShape(_radius, 2.0f * _halfHeight); | ||
| 746 | _ghostObject->setCollisionShape(_convexShape); | ||
| 747 | _ghostObject->setCollisionFlags(btCollisionObject::CF_CHARACTER_OBJECT); | ||
| 748 | } else { | ||
| 749 | // TODO: handle this failure case | ||
| 750 | } | ||
| 751 | _pendingFlags &= ~ PENDING_FLAG_UPDATE_SHAPE; | ||
| 745 | } | 752 | } |
| 753 | */ | ||
| 746 | 754 | ||
| 747 | bool CharacterController::needsShapeUpdate() { | 755 | void CharacterController::setLocalBoundingBox(const glm::vec3& corner, const glm::vec3& scale) { |
| 748 | // get new dimensions from avatar | 756 | _boxScale = scale; |
| 749 | _avatarData->lockForRead(); | ||
| 750 | AABox box = _avatarData->getLocalAABox(); | ||
| 751 | _avatarData->unlock(); | ||
| 752 | 757 | ||
| 753 | const glm::vec3& diagonal = box.getScale(); | 758 | float x = _boxScale.x; |
| 754 | float radius = 0.5f * sqrtf(0.5f * (diagonal.x * diagonal.x + diagonal.z * diagonal.z)); | 759 | float z = _boxScale.z; |
| 755 | float halfHeight = 0.5f * diagonal.y - radius; | 760 | float radius = 0.5f * sqrtf(0.5f * (x * x + z * z)); |
| 761 | float halfHeight = 0.5f * _boxScale.y - radius; | ||
| 756 | float MIN_HALF_HEIGHT = 0.1f; | 762 | float MIN_HALF_HEIGHT = 0.1f; |
| 757 | if (halfHeight < MIN_HALF_HEIGHT) { | 763 | if (halfHeight < MIN_HALF_HEIGHT) { |
| 758 | halfHeight = MIN_HALF_HEIGHT; | 764 | halfHeight = MIN_HALF_HEIGHT; |
| 759 | } | 765 | } |
| 760 | glm::vec3 offset = box.getCorner() + 0.5f * diagonal; | ||
| 761 | 766 | ||
| 762 | // compare dimensions (and offset) | 767 | // compare dimensions |
| 763 | float radiusDelta = glm::abs(radius - _radius); | 768 | float radiusDelta = glm::abs(radius - _radius); |
| 764 | float heightDelta = glm::abs(halfHeight - _halfHeight); | 769 | float heightDelta = glm::abs(halfHeight - _halfHeight); |
| 765 | if (radiusDelta < FLT_EPSILON && heightDelta < FLT_EPSILON) { | 770 | if (radiusDelta < FLT_EPSILON && heightDelta < FLT_EPSILON) { |
| 766 | // shape hasn't changed --> nothing to do | 771 | // shape hasn't changed --> nothing to do |
| 767 | float offsetDelta = glm::distance(offset, _shapeLocalOffset); | 772 | } else { |
| 768 | if (offsetDelta > FLT_EPSILON) { | 773 | // we need to: remove, update, add |
| 769 | // if only the offset changes then we can update it --> no need to rebuild shape | 774 | _pendingFlags |= PENDING_FLAG_REMOVE_FROM_SIMULATION |
| 770 | _shapeLocalOffset = offset; | 775 | | PENDING_FLAG_UPDATE_SHAPE |
| 771 | } | 776 | | PENDING_FLAG_ADD_TO_SIMULATION; |
| 772 | return false; | ||
| 773 | } | 777 | } |
| 774 | return true; | ||
| 775 | } | ||
| 776 | 778 | ||
| 777 | void CharacterController::updateShape() { | 779 | // it's ok to change offset immediately -- there are no thread safety issues here |
| 778 | // DANGER: make sure this CharacterController and its GhostShape have been removed from | 780 | _shapeLocalOffset = corner + 0.5f * _boxScale; |
| 779 | // the PhysicsEngine before calling this. | 781 | } |
| 780 | 782 | ||
| 781 | // delete shape and GhostObject | 783 | bool CharacterController::needsAddition() const { |
| 782 | delete _ghostObject; | 784 | return (bool)(_pendingFlags & PENDING_FLAG_ADD_TO_SIMULATION); |
| 783 | _ghostObject = NULL; | ||
| 784 | delete _convexShape; | ||
| 785 | _convexShape = NULL; | ||
| 786 | |||
| 787 | createShapeAndGhost(); | ||
| 788 | } | 785 | } |
| 789 | 786 | ||
| 790 | void CharacterController::preSimulation(btScalar timeStep) { | 787 | bool CharacterController::needsRemoval() const { |
| 791 | bool wasEnabled = _enabled; | 788 | return (bool)(_pendingFlags & PENDING_FLAG_REMOVE_FROM_SIMULATION); |
| 789 | } | ||
| 792 | 790 | ||
| 793 | // lock avatarData, get everything we need from it ASAP, then unlock | 791 | void CharacterController::setEnabled(bool enabled) { |
| 794 | _avatarData->lockForRead(); | 792 | if (enabled != _enabled) { |
| 795 | _enabled = _avatarData->isPhysicsEnabled(); | 793 | if (enabled) { |
| 796 | glm::quat rotation = _avatarData->getOrientation(); | 794 | _pendingFlags |= PENDING_FLAG_ADD_TO_SIMULATION; |
| 797 | glm::vec3 position = _avatarData->getPosition() + rotation * _shapeLocalOffset; | 795 | } else { |
| 798 | // TODO: Andrew to implement: harvest jump event here | 796 | _pendingFlags |= PENDING_FLAG_REMOVE_FROM_SIMULATION; |
| 799 | btVector3 walkVelocity = glmToBullet(_avatarData->getVelocity()); | 797 | _pendingFlags &= ~ PENDING_FLAG_ADD_TO_SIMULATION; |
| 798 | } | ||
| 799 | _enabled = enabled; | ||
| 800 | } | ||
| 801 | } | ||
| 800 | 802 | ||
| 801 | _avatarData->unlock(); | 803 | void CharacterController::setDynamicsWorld(btDynamicsWorld* world) { |
| 804 | if (_dynamicsWorld != world) { | ||
| 805 | if (_dynamicsWorld) { | ||
| 806 | _dynamicsWorld->removeCollisionObject(getGhostObject()); | ||
| 807 | _dynamicsWorld->removeAction(this); | ||
| 808 | } | ||
| 809 | _dynamicsWorld = world; | ||
| 810 | if (_dynamicsWorld) { | ||
| 811 | _pendingFlags &= ~ PENDING_FLAG_ADD_TO_SIMULATION; | ||
| 812 | _dynamicsWorld->addCollisionObject(getGhostObject(), | ||
| 813 | btBroadphaseProxy::CharacterFilter, | ||
| 814 | btBroadphaseProxy::StaticFilter | btBroadphaseProxy::DefaultFilter); | ||
| 815 | _dynamicsWorld->addAction(this); | ||
| 816 | reset(_dynamicsWorld); | ||
| 817 | } else { | ||
| 818 | _pendingFlags &= ~ PENDING_FLAG_REMOVE_FROM_SIMULATION; | ||
| 819 | } | ||
| 820 | } else { | ||
| 821 | _pendingFlags &= ~ (PENDING_FLAG_REMOVE_FROM_SIMULATION | PENDING_FLAG_ADD_TO_SIMULATION); | ||
| 822 | } | ||
| 823 | } | ||
| 802 | 824 | ||
| 803 | if (wasEnabled != _enabled) { | 825 | void CharacterController::updateShape() { |
| 804 | if (_enabled) { | 826 | if (_pendingFlags & PENDING_FLAG_UPDATE_SHAPE) { |
| 805 | // TODO: Andrew to implement: add collision shape back into world | 827 | assert(!(_pendingFlags & PENDING_FLAG_REMOVE_FROM_SIMULATION)); |
| 828 | _pendingFlags &= ~ PENDING_FLAG_UPDATE_SHAPE; | ||
| 829 | // make sure there is NO pending removal from simulation at this point | ||
| 830 | // (don't want to delete _ghostObject out from under the simulation) | ||
| 831 | // delete shape and GhostObject | ||
| 832 | delete _ghostObject; | ||
| 833 | _ghostObject = NULL; | ||
| 834 | delete _convexShape; | ||
| 835 | _convexShape = NULL; | ||
| 836 | |||
| 837 | // compute new dimensions from avatar's bounding box | ||
| 838 | float x = _boxScale.x; | ||
| 839 | float z = _boxScale.z; | ||
| 840 | _radius = 0.5f * sqrtf(0.5f * (x * x + z * z)); | ||
| 841 | _halfHeight = 0.5f * _boxScale.y - _radius; | ||
| 842 | float MIN_HALF_HEIGHT = 0.1f; | ||
| 843 | if (_halfHeight < MIN_HALF_HEIGHT) { | ||
| 844 | _halfHeight = MIN_HALF_HEIGHT; | ||
| 845 | } | ||
| 846 | // NOTE: _shapeLocalOffset is already computed | ||
| 847 | |||
| 848 | if (_radius > 0.0f) { | ||
| 849 | // create new ghost | ||
| 850 | _ghostObject = new btPairCachingGhostObject(); | ||
| 851 | _ghostObject->setWorldTransform(btTransform(glmToBullet(_avatarData->getOrientation()), | ||
| 852 | glmToBullet(_avatarData->getPosition()))); | ||
| 853 | // stepHeight affects the heights of ledges that the character can ascend | ||
| 854 | // however the actual ledge height is some function of _stepHeight | ||
| 855 | // due to character shape and this CharacterController algorithm | ||
| 856 | // (the function is approximately 2*_stepHeight) | ||
| 857 | _stepHeight = 0.1f * (_radius + _halfHeight) + 0.1f; | ||
| 858 | |||
| 859 | // create new shape | ||
| 860 | _convexShape = new btCapsuleShape(_radius, 2.0f * _halfHeight); | ||
| 861 | _ghostObject->setCollisionShape(_convexShape); | ||
| 862 | _ghostObject->setCollisionFlags(btCollisionObject::CF_CHARACTER_OBJECT); | ||
| 806 | } else { | 863 | } else { |
| 807 | // TODO: Andrew to implement: remove collision shape from world, | 864 | // TODO: handle this failure case |
| 808 | // otherwise things will continue to collide with it | ||
| 809 | } | 865 | } |
| 810 | } | 866 | } |
| 867 | } | ||
| 868 | |||
| 869 | void CharacterController::preSimulation(btScalar timeStep) { | ||
| 870 | if (_enabled && _dynamicsWorld) { | ||
| 871 | glm::quat rotation = _avatarData->getOrientation(); | ||
| 872 | glm::vec3 position = _avatarData->getPosition() + rotation * _shapeLocalOffset; | ||
| 873 | // TODO: harvest jump event here | ||
| 874 | btVector3 walkVelocity = glmToBullet(_avatarData->getVelocity()); | ||
| 811 | 875 | ||
| 812 | if (_enabled) { | ||
| 813 | _ghostObject->setWorldTransform(btTransform(glmToBullet(rotation), glmToBullet(position))); | 876 | _ghostObject->setWorldTransform(btTransform(glmToBullet(rotation), glmToBullet(position))); |
| 814 | setVelocityForTimeInterval(walkVelocity, timeStep); | 877 | setVelocityForTimeInterval(walkVelocity, timeStep); |
| 815 | } | 878 | } |
| @@ -817,13 +880,11 @@ void CharacterController::preSimulation(btScalar timeStep) { · 1 unmodified line | |||
| 817 | 880 | ||
| 818 | void CharacterController::postSimulation() { | 881 | void CharacterController::postSimulation() { |
| 819 | if (_enabled) { | 882 | if (_enabled) { |
| 820 | _avatarData->lockForWrite(); | ||
| 821 | const btTransform& avatarTransform = _ghostObject->getWorldTransform(); | 883 | const btTransform& avatarTransform = _ghostObject->getWorldTransform(); |
| 822 | glm::quat rotation = bulletToGLM(avatarTransform.getRotation()); | 884 | glm::quat rotation = bulletToGLM(avatarTransform.getRotation()); |
| 823 | glm::vec3 offset = rotation * _shapeLocalOffset; | 885 | glm::vec3 offset = rotation * _shapeLocalOffset; |
| 824 | _avatarData->setOrientation(rotation); | 886 | _avatarData->setOrientation(rotation); |
| 825 | _avatarData->setPosition(bulletToGLM(avatarTransform.getOrigin()) - offset); | 887 | _avatarData->setPosition(bulletToGLM(avatarTransform.getOrigin()) - offset); |
| 826 | _avatarData->unlock(); | ||
| 827 | } | 888 | } |
| 828 | } | 889 | } |
| 829 | 890 | ||