Merge pull request #2821 from AndrewMeadows/inertia

avatar walks up ledges
This commit is contained in:
Philip Rosedale 2014-05-08 19:40:09 -07:00
commit 14bab0adbc
2 changed files with 77 additions and 22 deletions

View file

@ -46,6 +46,9 @@ const float PITCH_SPEED = 100.0f; // degrees/sec
const float COLLISION_RADIUS_SCALAR = 1.2f; // pertains to avatar-to-avatar collisions
const float COLLISION_RADIUS_SCALE = 0.125f;
const float MIN_KEYBOARD_CONTROL_SPEED = 2.0f;
const float MAX_WALKING_SPEED = 3.0f * MIN_KEYBOARD_CONTROL_SPEED;
const float DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS = 5.0f * 1000.0f;
// TODO: normalize avatar speed for standard avatar size, then scale all motion logic
@ -64,7 +67,7 @@ MyAvatar::MyAvatar() :
_distanceToNearestAvatar(std::numeric_limits<float>::max()),
_wasPushing(false),
_isPushing(false),
_wasStuck(false),
_trapDuration(0.0f),
_thrust(0.0f),
_motorVelocity(0.0f),
_motorTimescale(DEFAULT_MOTOR_TIMESCALE),
@ -226,7 +229,7 @@ void MyAvatar::simulate(float deltaTime) {
if (_collisionGroups & COLLISION_GROUP_VOXELS) {
updateCollisionWithVoxels(deltaTime, radius);
} else {
_wasStuck = false;
_trapDuration = 0.0f;
}
if (_collisionGroups & COLLISION_GROUP_AVATARS) {
updateCollisionWithAvatars(deltaTime);
@ -718,8 +721,6 @@ void MyAvatar::updateMotorFromKeyboard(float deltaTime, bool walking) {
if (directionLength > EPSILON) {
direction /= directionLength;
// the finalMotorSpeed depends on whether we are walking or not
const float MIN_KEYBOARD_CONTROL_SPEED = 2.0f;
const float MAX_WALKING_SPEED = 3.0f * MIN_KEYBOARD_CONTROL_SPEED;
float finalMaxMotorSpeed = walking ? MAX_WALKING_SPEED : _maxMotorSpeed;
float motorLength = glm::length(_motorVelocity);
@ -949,48 +950,102 @@ void MyAvatar::updateCollisionWithEnvironment(float deltaTime, float radius) {
static CollisionList myCollisions(64);
void MyAvatar::updateCollisionWithVoxels(float deltaTime, float radius) {
const float MIN_STUCK_SPEED = 100.0f;
const float MAX_VOXEL_COLLISION_SPEED = 100.0f;
float speed = glm::length(_velocity);
if (speed > MIN_STUCK_SPEED) {
if (speed > MAX_VOXEL_COLLISION_SPEED) {
// don't even bother to try to collide against voxles when moving very fast
_trapDuration = 0.0f;
return;
}
bool isTrapped = false;
myCollisions.clear();
const CapsuleShape& boundingShape = _skeletonModel.getBoundingShape();
if (Application::getInstance()->getVoxelTree()->findShapeCollisions(&boundingShape, myCollisions)) {
if (Application::getInstance()->getVoxelTree()->findShapeCollisions(&boundingShape, myCollisions, Octree::TryLock)) {
const float VOXEL_ELASTICITY = 0.0f;
const float VOXEL_DAMPING = 0.0f;
float capsuleRadius = boundingShape.getRadius();
float capsuleHalfHeight = boundingShape.getHalfHeight();
const float MAX_STEP_HEIGHT = capsuleRadius + capsuleHalfHeight;
const float MIN_STEP_HEIGHT = 0.0f;
glm::vec3 footBase = boundingShape.getPosition() - (capsuleRadius + capsuleHalfHeight) * _worldUpDirection;
float highestStep = 0.0f;
glm::vec3 stepPenetration(0.0f);
glm::vec3 totalPenetration(0.0f);
bool isStuck = false;
for (int i = 0; i < myCollisions.size(); ++i) {
CollisionInfo* collision = myCollisions[i];
float depth = glm::length(collision->_penetration);
if (depth > capsuleRadius) {
isStuck = true;
if (_wasStuck) {
glm::vec3 cubeCenter = collision->_vecData;
float cubeSide = collision->_floatData;
glm::vec3 cubeCenter = collision->_vecData;
float cubeSide = collision->_floatData;
float verticalDepth = glm::dot(collision->_penetration, _worldUpDirection);
float horizontalDepth = glm::length(collision->_penetration - verticalDepth * _worldUpDirection);
const float MAX_TRAP_PERIOD = 0.125f;
if (horizontalDepth > capsuleRadius || fabsf(verticalDepth) > MAX_STEP_HEIGHT) {
isTrapped = true;
if (_trapDuration > MAX_TRAP_PERIOD) {
float distance = glm::dot(boundingShape.getPosition() - cubeCenter, _worldUpDirection);
if (distance < 0.0f) {
distance = fabsf(distance) + 0.5f * cubeSide;
}
distance += capsuleRadius + boundingShape.getHalfHeight();
distance += capsuleRadius + capsuleHalfHeight;
totalPenetration = addPenetrations(totalPenetration, - distance * _worldUpDirection);
continue;
}
} else if (_trapDuration > MAX_TRAP_PERIOD) {
// we're trapped, ignore this collision
continue;
}
totalPenetration = addPenetrations(totalPenetration, collision->_penetration);
if (glm::dot(collision->_penetration, _velocity) >= 0.0f) {
glm::vec3 cubeTop = cubeCenter + (0.5f * cubeSide) * _worldUpDirection;
float stepHeight = glm::dot(_worldUpDirection, cubeTop - footBase);
if (stepHeight > highestStep) {
highestStep = stepHeight;
stepPenetration = collision->_penetration;
}
}
}
float penetrationLength = glm::length(totalPenetration);
if (penetrationLength < EPSILON) {
_trapDuration = 0.0f;
return;
}
float verticalPenetration = glm::dot(totalPenetration, _worldUpDirection);
if (highestStep > MIN_STEP_HEIGHT && highestStep < MAX_STEP_HEIGHT && verticalPenetration <= 0.0f) {
// we're colliding against an edge
glm::vec3 targetVelocity = _motorVelocity;
if (_motionBehaviors & AVATAR_MOTION_MOTOR_USE_LOCAL_FRAME) {
// rotate _motorVelocity into world frame
glm::quat rotation = getHead()->getCameraOrientation();
targetVelocity = rotation * _motorVelocity;
}
if (_wasPushing && glm::dot(targetVelocity, totalPenetration) > EPSILON) {
// we're puhing into the edge, so we want to lift
// remove unhelpful horizontal component of the step's penetration
totalPenetration -= stepPenetration - (glm::dot(stepPenetration, _worldUpDirection) * _worldUpDirection);
// further adjust penetration to help lift
float liftSpeed = glm::max(MAX_WALKING_SPEED, speed);
float thisStep = glm::min(liftSpeed * deltaTime, highestStep);
float extraStep = glm::dot(totalPenetration, _worldUpDirection) + thisStep;
if (extraStep > 0.0f) {
totalPenetration -= extraStep * _worldUpDirection;
}
_position -= totalPenetration;
} else {
// we're not pushing into the edge, so let the avatar fall
applyHardCollision(totalPenetration, VOXEL_ELASTICITY, VOXEL_DAMPING);
}
} else {
applyHardCollision(totalPenetration, VOXEL_ELASTICITY, VOXEL_DAMPING);
}
applyHardCollision(totalPenetration, VOXEL_ELASTICITY, VOXEL_DAMPING);
_wasStuck = isStuck;
const float VOXEL_COLLISION_FREQUENCY = 0.5f;
updateCollisionSound(myCollisions[0]->_penetration, deltaTime, VOXEL_COLLISION_FREQUENCY);
} else {
_wasStuck = false;
}
}
_trapDuration = isTrapped ? _trapDuration + deltaTime : 0.0f;
}
void MyAvatar::applyHardCollision(const glm::vec3& penetration, float elasticity, float damping) {

View file

@ -126,7 +126,7 @@ private:
bool _wasPushing;
bool _isPushing;
bool _wasStuck;
float _trapDuration; // seconds that avatar has been trapped by collisions
glm::vec3 _thrust; // final acceleration from outside sources for the current frame
glm::vec3 _motorVelocity; // intended velocity of avatar motion