From 69e279e2c6d381f4588e38268a0c1ed06aa4feb5 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 29 Apr 2014 14:05:16 -0700 Subject: [PATCH 01/47] IC-1, remove some spaces in BuckyBalls --- interface/src/BuckyBalls.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/interface/src/BuckyBalls.h b/interface/src/BuckyBalls.h index f7690408d3..d46245c9bf 100644 --- a/interface/src/BuckyBalls.h +++ b/interface/src/BuckyBalls.h @@ -23,7 +23,6 @@ #include "InterfaceConfig.h" #include "Util.h" - const int NUM_BBALLS = 200; class BuckyBalls { @@ -42,9 +41,7 @@ private: float _bballRadius[NUM_BBALLS]; float _bballColliding[NUM_BBALLS]; int _bballElement[NUM_BBALLS]; - int _bballIsGrabbed[2]; - - + int _bballIsGrabbed[2]; }; #endif // hifi_BuckyBalls_h From 4e34d0339d9eee1c4d4bc14ed2eed67d4afc5a04 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 29 Apr 2014 15:15:53 -0700 Subject: [PATCH 02/47] set position in ctor that takes endpoints and some cleanup --- libraries/shared/src/CapsuleShape.cpp | 5 +++-- libraries/shared/src/CapsuleShape.h | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/shared/src/CapsuleShape.cpp b/libraries/shared/src/CapsuleShape.cpp index 5055b3636e..11bd70f8d2 100644 --- a/libraries/shared/src/CapsuleShape.cpp +++ b/libraries/shared/src/CapsuleShape.cpp @@ -34,6 +34,7 @@ CapsuleShape::CapsuleShape(float radius, float halfHeight, const glm::vec3& posi CapsuleShape::CapsuleShape(float radius, const glm::vec3& startPoint, const glm::vec3& endPoint) : Shape(Shape::CAPSULE_SHAPE), _radius(radius), _halfHeight(0.0f) { glm::vec3 axis = endPoint - startPoint; + _position = 0.5f * (endPoint + startPoint); float height = glm::length(axis); if (height > EPSILON) { _halfHeight = 0.5f * height; @@ -50,12 +51,12 @@ CapsuleShape::CapsuleShape(float radius, const glm::vec3& startPoint, const glm: /// \param[out] startPoint is the center of start cap void CapsuleShape::getStartPoint(glm::vec3& startPoint) const { - startPoint = getPosition() - _rotation * glm::vec3(0.0f, _halfHeight, 0.0f); + startPoint = _position - _rotation * glm::vec3(0.0f, _halfHeight, 0.0f); } /// \param[out] endPoint is the center of the end cap void CapsuleShape::getEndPoint(glm::vec3& endPoint) const { - endPoint = getPosition() + _rotation * glm::vec3(0.0f, _halfHeight, 0.0f); + endPoint = _position + _rotation * glm::vec3(0.0f, _halfHeight, 0.0f); } void CapsuleShape::computeNormalizedAxis(glm::vec3& axis) const { diff --git a/libraries/shared/src/CapsuleShape.h b/libraries/shared/src/CapsuleShape.h index 9421bf1789..0889f6b2f3 100644 --- a/libraries/shared/src/CapsuleShape.h +++ b/libraries/shared/src/CapsuleShape.h @@ -14,7 +14,6 @@ #include "Shape.h" -// adebug bookmark TODO: convert to new world-frame approach // default axis of CapsuleShape is Y-axis class CapsuleShape : public Shape { From 34aaf98b535f61ce6bea1cb6841bc1720e3229fb Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 29 Apr 2014 15:16:50 -0700 Subject: [PATCH 03/47] cleanup around how boundary shape is computed --- interface/src/renderer/Model.cpp | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 3484ac5fc8..43beca5cd6 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -542,18 +542,10 @@ void Model::rebuildShapes() { capsule->setRotation(combinedRotations[i] * joint.shapeRotation); _jointShapes.push_back(capsule); - glm::vec3 endPoint; - capsule->getEndPoint(endPoint); - glm::vec3 startPoint; - capsule->getStartPoint(startPoint); - - // add some points that bound a sphere at the center of the capsule - glm::vec3 axis = glm::vec3(radius); - shapeExtents.addPoint(worldPosition + axis); - shapeExtents.addPoint(worldPosition - axis); - // add the two furthest surface points of the capsule - axis = (halfHeight + radius) * glm::normalize(endPoint - startPoint); + glm::vec3 axis; + capsule->computeNormalizedAxis(axis); + axis = halfHeight * axis + glm::vec3(radius); shapeExtents.addPoint(worldPosition + axis); shapeExtents.addPoint(worldPosition - axis); @@ -587,7 +579,7 @@ void Model::rebuildShapes() { glm::quat inverseRotation = glm::inverse(_rotation); glm::vec3 rootPosition = extractTranslation(transforms[rootIndex]); _boundingShapeLocalOffset = inverseRotation * (0.5f * (totalExtents.maximum + totalExtents.minimum) - rootPosition); - _boundingShape.setPosition(_translation - _rotation * _boundingShapeLocalOffset); + _boundingShape.setPosition(_translation + _rotation * _boundingShapeLocalOffset); _boundingShape.setRotation(_rotation); } From db9221da6e8b3a8b35761c6f706bf8471f8c23c9 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 30 Apr 2014 10:38:42 -0700 Subject: [PATCH 04/47] fix misspelled slot --- interface/src/Menu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index ff3c6a1f43..66d3e7acb4 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -189,7 +189,7 @@ Menu::Menu() : addDisabledActionAndSeparator(editMenu, "Physics"); QObject* avatar = appInstance->getAvatar(); addCheckableActionToQMenuAndActionHash(editMenu, MenuOption::ObeyEnvironmentalGravity, Qt::SHIFT | Qt::Key_G, true, - avatar, SLOT(updateMotionBehaviors())); + avatar, SLOT(updateMotionBehaviorsFromMenu())); addAvatarCollisionSubMenu(editMenu); From 8d4dca31c56a915a88acb5d6150702eaf3f0a8b1 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 1 May 2014 12:05:34 -0700 Subject: [PATCH 05/47] add velocity model for controlling avatar motion --- interface/src/avatar/MyAvatar.cpp | 322 +++++++++++++++++++---------- interface/src/avatar/MyAvatar.h | 21 +- libraries/avatars/src/AvatarData.h | 20 +- 3 files changed, 243 insertions(+), 120 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 40e350dcb7..2e27706820 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -45,7 +45,10 @@ 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 DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS = 5 * 1000; +const float DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS = 5.0f * 1000.0f; + +float DEFAULT_MOTOR_TIMESCALE = 0.25f; +float DEFAULT_MAX_MOTOR_SPEED = 300.0f; // TODO: normalize this based on avatar height MyAvatar::MyAvatar() : Avatar(), @@ -60,7 +63,10 @@ MyAvatar::MyAvatar() : _thrust(0.0f), _isThrustOn(false), _thrustMultiplier(1.0f), - _motionBehaviors(0), + _motorVelocity(0.0f), + _motorTimescale(DEFAULT_MOTOR_TIMESCALE), + _maxMotorSpeed(DEFAULT_MAX_MOTOR_SPEED), + _motionBehaviors(AVATAR_MOTION_DEFAULTS), _lastBodyPenetration(0.0f), _lookAtTargetAvatar(), _shouldRender(true), @@ -134,17 +140,12 @@ void MyAvatar::update(float deltaTime) { void MyAvatar::simulate(float deltaTime) { - glm::quat orientation = getOrientation(); - if (_scale != _targetScale) { float scale = (1.0f - SMOOTHING_RATIO) * _scale + SMOOTHING_RATIO * _targetScale; setScale(scale); Application::getInstance()->getCamera()->setScale(scale); } - // Collect thrust forces from keyboard and devices - updateThrust(deltaTime); - // update the movement of the hand and process handshaking with other avatars... updateHandMovementAndTouching(deltaTime); @@ -156,105 +157,19 @@ void MyAvatar::simulate(float deltaTime) { _velocity += _scale * _gravity * (GRAVITY_EARTH * deltaTime); } - // add thrust to velocity - _velocity += _thrust * deltaTime; + updateOrientation(deltaTime); - // update body yaw by body yaw delta - orientation = orientation * glm::quat(glm::radians( - glm::vec3(_bodyPitchDelta, _bodyYawDelta, _bodyRollDelta) * deltaTime)); - // decay body rotation momentum + rampMotor(deltaTime); + applyMotor(deltaTime); - const float BODY_SPIN_FRICTION = 7.5f; - float bodySpinMomentum = 1.0f - BODY_SPIN_FRICTION * deltaTime; - if (bodySpinMomentum < 0.0f) { bodySpinMomentum = 0.0f; } - _bodyPitchDelta *= bodySpinMomentum; - _bodyYawDelta *= bodySpinMomentum; - _bodyRollDelta *= bodySpinMomentum; + // zero thrust so we don't pile up thrust from other sources + _thrust = glm::vec3(0.0f); - float MINIMUM_ROTATION_RATE = 2.0f; - if (fabs(_bodyYawDelta) < MINIMUM_ROTATION_RATE) { _bodyYawDelta = 0.0f; } - if (fabs(_bodyRollDelta) < MINIMUM_ROTATION_RATE) { _bodyRollDelta = 0.0f; } - if (fabs(_bodyPitchDelta) < MINIMUM_ROTATION_RATE) { _bodyPitchDelta = 0.0f; } - - const float MAX_STATIC_FRICTION_SPEED = 0.5f; - const float STATIC_FRICTION_STRENGTH = _scale * 20.0f; - applyStaticFriction(deltaTime, _velocity, MAX_STATIC_FRICTION_SPEED, STATIC_FRICTION_STRENGTH); - - // Damp avatar velocity - const float LINEAR_DAMPING_STRENGTH = 0.5f; - const float SPEED_BRAKE_POWER = _scale * 10.0f; - const float SQUARED_DAMPING_STRENGTH = 0.007f; - - const float SLOW_NEAR_RADIUS = 5.0f; - float linearDamping = LINEAR_DAMPING_STRENGTH; - const float NEAR_AVATAR_DAMPING_FACTOR = 50.0f; - if (_distanceToNearestAvatar < _scale * SLOW_NEAR_RADIUS) { - linearDamping *= 1.0f + NEAR_AVATAR_DAMPING_FACTOR * - ((SLOW_NEAR_RADIUS - _distanceToNearestAvatar) / SLOW_NEAR_RADIUS); - } - if (_speedBrakes) { - applyDamping(deltaTime, _velocity, linearDamping * SPEED_BRAKE_POWER, SQUARED_DAMPING_STRENGTH * SPEED_BRAKE_POWER); - } else { - applyDamping(deltaTime, _velocity, linearDamping, SQUARED_DAMPING_STRENGTH); - } - - if (OculusManager::isConnected()) { - // these angles will be in radians - float yaw, pitch, roll; - OculusManager::getEulerAngles(yaw, pitch, roll); - // ... so they need to be converted to degrees before we do math... - - // The neck is limited in how much it can yaw, so we check its relative - // yaw from the body and yaw the body if necessary. - yaw *= DEGREES_PER_RADIAN; - float bodyToHeadYaw = yaw - _oculusYawOffset; - const float MAX_NECK_YAW = 85.0f; // degrees - if ((fabs(bodyToHeadYaw) > 2.0f * MAX_NECK_YAW) && (yaw * _oculusYawOffset < 0.0f)) { - // We've wrapped around the range for yaw so adjust - // the measured yaw to be relative to _oculusYawOffset. - if (yaw > 0.0f) { - yaw -= 360.0f; - } else { - yaw += 360.0f; - } - bodyToHeadYaw = yaw - _oculusYawOffset; - } - - float delta = fabs(bodyToHeadYaw) - MAX_NECK_YAW; - if (delta > 0.0f) { - yaw = MAX_NECK_YAW; - if (bodyToHeadYaw < 0.0f) { - delta *= -1.0f; - bodyToHeadYaw = -MAX_NECK_YAW; - } else { - bodyToHeadYaw = MAX_NECK_YAW; - } - // constrain _oculusYawOffset to be within range [-180,180] - _oculusYawOffset = fmod((_oculusYawOffset + delta) + 180.0f, 360.0f) - 180.0f; - - // We must adjust the body orientation using a delta rotation (rather than - // doing yaw math) because the body's yaw ranges are not the same - // as what the Oculus API provides. - glm::vec3 UP_AXIS = glm::vec3(0.0f, 1.0f, 0.0f); - glm::quat bodyCorrection = glm::angleAxis(glm::radians(delta), UP_AXIS); - orientation = orientation * bodyCorrection; - } - Head* head = getHead(); - head->setBaseYaw(bodyToHeadYaw); - - head->setBasePitch(pitch * DEGREES_PER_RADIAN); - head->setBaseRoll(roll * DEGREES_PER_RADIAN); - } - - // update the euler angles - setOrientation(orientation); + updateChatCircle(deltaTime); // update moving flag based on speed const float MOVING_SPEED_THRESHOLD = 0.01f; - float speed = glm::length(_velocity); - _moving = speed > MOVING_SPEED_THRESHOLD; - - updateChatCircle(deltaTime); + _moving = glm::length(_velocity) > MOVING_SPEED_THRESHOLD; _position += _velocity * deltaTime; @@ -280,9 +195,6 @@ void MyAvatar::simulate(float deltaTime) { head->setScale(_scale); head->simulate(deltaTime, true); - // Zero thrust out now that we've added it to velocity in this frame - _thrust *= glm::vec3(0.0f); - // now that we're done stepping the avatar forward in time, compute new collisions if (_collisionGroups != 0) { Camera* myCamera = Application::getInstance()->getCamera(); @@ -643,6 +555,167 @@ bool MyAvatar::shouldRenderHead(const glm::vec3& cameraPosition, RenderMode rend (glm::length(cameraPosition - head->calculateAverageEyePosition()) > RENDER_HEAD_CUTOFF_DISTANCE * _scale); } +void MyAvatar::updateOrientation(float deltaTime) { + // Gather rotation information from keyboard + _bodyYawDelta -= _driveKeys[ROT_RIGHT] * YAW_SPEED * deltaTime; + _bodyYawDelta += _driveKeys[ROT_LEFT] * YAW_SPEED * deltaTime; + getHead()->setBasePitch(getHead()->getBasePitch() + (_driveKeys[ROT_UP] - _driveKeys[ROT_DOWN]) * PITCH_SPEED * deltaTime); + + // update body yaw by body yaw delta + glm::quat orientation = getOrientation() * glm::quat(glm::radians( + glm::vec3(_bodyPitchDelta, _bodyYawDelta, _bodyRollDelta) * deltaTime)); + + // decay body rotation momentum + const float BODY_SPIN_FRICTION = 7.5f; + float bodySpinMomentum = 1.0f - BODY_SPIN_FRICTION * deltaTime; + if (bodySpinMomentum < 0.0f) { bodySpinMomentum = 0.0f; } + _bodyPitchDelta *= bodySpinMomentum; + _bodyYawDelta *= bodySpinMomentum; + _bodyRollDelta *= bodySpinMomentum; + + float MINIMUM_ROTATION_RATE = 2.0f; + if (fabs(_bodyYawDelta) < MINIMUM_ROTATION_RATE) { _bodyYawDelta = 0.0f; } + if (fabs(_bodyRollDelta) < MINIMUM_ROTATION_RATE) { _bodyRollDelta = 0.0f; } + if (fabs(_bodyPitchDelta) < MINIMUM_ROTATION_RATE) { _bodyPitchDelta = 0.0f; } + + if (OculusManager::isConnected()) { + // these angles will be in radians + float yaw, pitch, roll; + OculusManager::getEulerAngles(yaw, pitch, roll); + // ... so they need to be converted to degrees before we do math... + + // The neck is limited in how much it can yaw, so we check its relative + // yaw from the body and yaw the body if necessary. + yaw *= DEGREES_PER_RADIAN; + float bodyToHeadYaw = yaw - _oculusYawOffset; + const float MAX_NECK_YAW = 85.0f; // degrees + if ((fabs(bodyToHeadYaw) > 2.0f * MAX_NECK_YAW) && (yaw * _oculusYawOffset < 0.0f)) { + // We've wrapped around the range for yaw so adjust + // the measured yaw to be relative to _oculusYawOffset. + if (yaw > 0.0f) { + yaw -= 360.0f; + } else { + yaw += 360.0f; + } + bodyToHeadYaw = yaw - _oculusYawOffset; + } + + float delta = fabs(bodyToHeadYaw) - MAX_NECK_YAW; + if (delta > 0.0f) { + yaw = MAX_NECK_YAW; + if (bodyToHeadYaw < 0.0f) { + delta *= -1.0f; + bodyToHeadYaw = -MAX_NECK_YAW; + } else { + bodyToHeadYaw = MAX_NECK_YAW; + } + // constrain _oculusYawOffset to be within range [-180,180] + _oculusYawOffset = fmod((_oculusYawOffset + delta) + 180.0f, 360.0f) - 180.0f; + + // We must adjust the body orientation using a delta rotation (rather than + // doing yaw math) because the body's yaw ranges are not the same + // as what the Oculus API provides. + glm::vec3 UP_AXIS = glm::vec3(0.0f, 1.0f, 0.0f); + glm::quat bodyCorrection = glm::angleAxis(glm::radians(delta), UP_AXIS); + orientation = orientation * bodyCorrection; + } + Head* head = getHead(); + head->setBaseYaw(bodyToHeadYaw); + + head->setBasePitch(pitch * DEGREES_PER_RADIAN); + head->setBaseRoll(roll * DEGREES_PER_RADIAN); + } + + // update the euler angles + setOrientation(orientation); +} + +void MyAvatar::rampMotor(float deltaTime) { + // Increase motor velocity until its length is equal to _maxMotorSpeed. + if (!(_motionBehaviors & AVATAR_MOTION_MOTOR_RAMP_AND_BRAKES_ENABLED)) { + // nothing to do + return; + } + + glm::vec3 localVelocity = _velocity; + if (_motionBehaviors & AVATAR_MOTION_MOTOR_USE_LOCAL_FRAME) { + glm::quat orientation = getHead()->getCameraOrientation(); + localVelocity = glm::inverse(orientation) * _velocity; + } + + // Compute keyboard input + glm::vec3 front = (_driveKeys[FWD] - _driveKeys[BACK]) * IDENTITY_FRONT; + glm::vec3 right = (_driveKeys[RIGHT] - _driveKeys[LEFT]) * IDENTITY_RIGHT; + glm::vec3 up = (_driveKeys[UP] - _driveKeys[DOWN]) * IDENTITY_UP; + + glm::vec3 direction = front + right + up; + float directionLength = glm::length(direction); + + // Compute motor magnitude + if (directionLength > EPSILON) { + direction /= directionLength; + const float MIN_WALKING_SPEED = 2.0f; + float motorLength = glm::length(_motorVelocity); + if (motorLength < MIN_WALKING_SPEED) { + _motorVelocity = MIN_WALKING_SPEED * direction; + } else { + float MOTOR_LENGTH_TIMESCALE = 1.5f; + float tau = glm::clamp(deltaTime / MOTOR_LENGTH_TIMESCALE, 0.0f, 1.0f); + float INCREASE_FACTOR = 2.0f; + //_motorVelocity *= 1.0f + tau * INCREASE_FACTOR; + motorLength *= 1.0f + tau * INCREASE_FACTOR; + if (motorLength > _maxMotorSpeed) { + motorLength = _maxMotorSpeed; + } + _motorVelocity = motorLength * direction; + } + _speedBrakes = false; + } else { + // apply brakes + _speedBrakes = true; + _motorVelocity = - localVelocity; + } +} + +void MyAvatar::applyMotor(float deltaTime) { + if (!( _motionBehaviors & AVATAR_MOTION_MOTOR_ENABLED)) { + // nothing to do --> early exit + return; + } + glm::vec3 targetVelocity = _motorVelocity; + if (_motionBehaviors & AVATAR_MOTION_MOTOR_USE_LOCAL_FRAME) { + // rotate _motorVelocity into world frame + glm::quat rotation = getOrientation(); + targetVelocity = rotation * _motorVelocity; + } + + glm::vec3 targetDirection(0.f); + if (glm::length2(targetVelocity) > EPSILON) { + targetDirection = glm::normalize(targetVelocity); + } + glm::vec3 deltaVelocity = targetVelocity - _velocity; + + if (_motionBehaviors & AVATAR_MOTION_MOTOR_COLLISION_SURFACE_ONLY && glm::length2(_gravity) > EPSILON) { + // For now we subtract the component parallel to gravity but what we need to do is: + // TODO: subtract the component perp to the local surface normal (motor only pushes in surface plane). + glm::vec3 gravityDirection = glm::normalize(_gravity); + glm::vec3 parallelDelta = glm::dot(deltaVelocity, gravityDirection) * gravityDirection; + if (glm::dot(targetVelocity, _velocity) > 0.0f) { + // remove parallel part from deltaVelocity + deltaVelocity -= parallelDelta; + } + } + + float MIN_MOTOR_TIMESCALE = 0.25f; + float timescale = _speedBrakes ? MIN_MOTOR_TIMESCALE : _motorTimescale; + + // simple critical damping + float tau = glm::clamp(deltaTime / timescale, 0.0f, 1.0f); + _velocity += tau * deltaVelocity; +} + +/* Keep this code for the short term as reference in case we need to further tune + *the new velocity model to achieve similar movement response. void MyAvatar::updateThrust(float deltaTime) { // // Gather thrust information from keyboard and sensors to apply to avatar motion @@ -688,10 +761,6 @@ void MyAvatar::updateThrust(float deltaTime) { } _lastBodyPenetration = glm::vec3(0.0f); - _bodyYawDelta -= _driveKeys[ROT_RIGHT] * YAW_SPEED * deltaTime; - _bodyYawDelta += _driveKeys[ROT_LEFT] * YAW_SPEED * deltaTime; - getHead()->setBasePitch(getHead()->getBasePitch() + (_driveKeys[ROT_UP] - _driveKeys[ROT_DOWN]) * PITCH_SPEED * deltaTime); - // If thrust keys are being held down, slowly increase thrust to allow reaching great speeds if (_driveKeys[FWD] || _driveKeys[BACK] || _driveKeys[RIGHT] || _driveKeys[LEFT] || _driveKeys[UP] || _driveKeys[DOWN]) { const float THRUST_INCREASE_RATE = 1.05f; @@ -722,8 +791,34 @@ void MyAvatar::updateThrust(float deltaTime) { if (_isThrustOn || (_speedBrakes && (glm::length(_velocity) < MIN_SPEED_BRAKE_VELOCITY))) { _speedBrakes = false; } + _velocity += _thrust * deltaTime; + // Zero thrust out now that we've added it to velocity in this frame + _thrust = glm::vec3(0.0f); + + // apply linear damping + const float MAX_STATIC_FRICTION_SPEED = 0.5f; + const float STATIC_FRICTION_STRENGTH = _scale * 20.0f; + applyStaticFriction(deltaTime, _velocity, MAX_STATIC_FRICTION_SPEED, STATIC_FRICTION_STRENGTH); + + const float LINEAR_DAMPING_STRENGTH = 0.5f; + const float SPEED_BRAKE_POWER = _scale * 10.0f; + const float SQUARED_DAMPING_STRENGTH = 0.007f; + + const float SLOW_NEAR_RADIUS = 5.0f; + float linearDamping = LINEAR_DAMPING_STRENGTH; + const float NEAR_AVATAR_DAMPING_FACTOR = 50.0f; + if (_distanceToNearestAvatar < _scale * SLOW_NEAR_RADIUS) { + linearDamping *= 1.0f + NEAR_AVATAR_DAMPING_FACTOR * + ((SLOW_NEAR_RADIUS - _distanceToNearestAvatar) / SLOW_NEAR_RADIUS); + } + if (_speedBrakes) { + applyDamping(deltaTime, _velocity, linearDamping * SPEED_BRAKE_POWER, SQUARED_DAMPING_STRENGTH * SPEED_BRAKE_POWER); + } else { + applyDamping(deltaTime, _velocity, linearDamping, SQUARED_DAMPING_STRENGTH); + } } +*/ void MyAvatar::updateHandMovementAndTouching(float deltaTime) { glm::quat orientation = getOrientation(); @@ -1151,8 +1246,7 @@ void MyAvatar::goToLocationFromResponse(const QJsonObject& jsonObject) { } } -void MyAvatar::updateMotionBehaviors() { - _motionBehaviors = 0; +void MyAvatar::updateMotionBehaviorsFromMenu() { if (Menu::getInstance()->isOptionChecked(MenuOption::ObeyEnvironmentalGravity)) { _motionBehaviors |= AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY; // Environmental and Local gravities are incompatible. Environmental setting trumps local. @@ -1172,8 +1266,14 @@ void MyAvatar::setCollisionGroups(quint32 collisionGroups) { menu->setIsOptionChecked(MenuOption::CollideWithParticles, (bool)(_collisionGroups & COLLISION_GROUP_PARTICLES)); } -void MyAvatar::setMotionBehaviors(quint32 flags) { - _motionBehaviors = flags; +void MyAvatar::setMotionBehaviorsByScript(quint32 flags) { + // start with the defaults + _motionBehaviors = AVATAR_MOTION_DEFAULTS; + + // add the set scriptable bits + _motionBehaviors += flags & AVATAR_MOTION_SCRIPTABLE_BITS; + + // reconcile incompatible settings from menu (if any) Menu* menu = Menu::getInstance(); menu->setIsOptionChecked(MenuOption::ObeyEnvironmentalGravity, (bool)(_motionBehaviors & AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY)); // Environmental and Local gravities are incompatible. Environmental setting trumps local. diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index a5312b0016..bfafffdc02 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -28,7 +28,7 @@ enum AvatarHandState class MyAvatar : public Avatar { Q_OBJECT Q_PROPERTY(bool shouldRenderLocally READ getShouldRenderLocally WRITE setShouldRenderLocally) - Q_PROPERTY(quint32 motionBehaviors READ getMotionBehaviors WRITE setMotionBehaviors) + Q_PROPERTY(quint32 motionBehaviors READ getMotionBehaviorsForScript WRITE setMotionBehaviorsByScript) Q_PROPERTY(glm::vec3 gravity READ getGravity WRITE setLocalGravity) public: @@ -90,8 +90,9 @@ public: virtual void setSkeletonModelURL(const QUrl& skeletonModelURL); virtual void setCollisionGroups(quint32 collisionGroups); - void setMotionBehaviors(quint32 flags); - quint32 getMotionBehaviors() const { return _motionBehaviors; } + + void setMotionBehaviorsByScript(quint32 flags); + quint32 getMotionBehaviorsForScript() const { return _motionBehaviors & AVATAR_MOTION_SCRIPTABLE_BITS; } void applyCollision(const glm::vec3& contactPoint, const glm::vec3& penetration); @@ -109,7 +110,7 @@ public slots: glm::vec3 getThrust() { return _thrust; }; void setThrust(glm::vec3 newThrust) { _thrust = newThrust; } - void updateMotionBehaviors(); + void updateMotionBehaviorsFromMenu(); signals: void transformChanged(); @@ -124,13 +125,17 @@ private: glm::vec3 _environmentGravity; float _distanceToNearestAvatar; // How close is the nearest avatar? - // motion stuff + // old motion stuff glm::vec3 _lastCollisionPosition; bool _speedBrakes; - glm::vec3 _thrust; // final acceleration for the current frame + glm::vec3 _thrust; // final acceleration from outside sources for the current frame bool _isThrustOn; float _thrustMultiplier; + // new motion stuff + glm::vec3 _motorVelocity; // intended velocity of avatar motion + float _motorTimescale; // timescale for avatar motion to kick in + float _maxMotorSpeed; quint32 _motionBehaviors; glm::vec3 _lastBodyPenetration; @@ -141,7 +146,9 @@ private: float _oculusYawOffset; // private methods - void updateThrust(float deltaTime); + void updateOrientation(float deltaTime); + void rampMotor(float deltaTime); + void applyMotor(float deltaTime); void updateHandMovementAndTouching(float deltaTime); void updateCollisionWithAvatars(float deltaTime); void updateCollisionWithEnvironment(float deltaTime, float radius); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index be47aed1ba..3c1aa9963d 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -51,8 +51,24 @@ typedef unsigned long long quint64; #include "HandData.h" // avatar motion behaviors -const quint32 AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY = 1U << 0; -const quint32 AVATAR_MOTION_OBEY_LOCAL_GRAVITY = 1U << 1; +const quint32 AVATAR_MOTION_MOTOR_ENABLED = 1U << 0; +const quint32 AVATAR_MOTION_MOTOR_RAMP_AND_BRAKES_ENABLED = 1U << 1; +const quint32 AVATAR_MOTION_MOTOR_USE_LOCAL_FRAME = 1U << 2; +const quint32 AVATAR_MOTION_MOTOR_COLLISION_SURFACE_ONLY = 1U << 3; + +const quint32 AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY = 1U << 4; +const quint32 AVATAR_MOTION_OBEY_LOCAL_GRAVITY = 1U << 5; + +const quint32 AVATAR_MOTION_DEFAULTS = + AVATAR_MOTION_MOTOR_ENABLED | + AVATAR_MOTION_MOTOR_RAMP_AND_BRAKES_ENABLED | + AVATAR_MOTION_MOTOR_USE_LOCAL_FRAME; + +// these bits will be expanded as features are exposed +const quint32 AVATAR_MOTION_SCRIPTABLE_BITS = + AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY | + AVATAR_MOTION_OBEY_LOCAL_GRAVITY; + // First bitset const int KEY_STATE_START_BIT = 0; // 1st and 2nd bits From b4d3df47fbfd89cb9bfc897b6846aa069588dba5 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 1 May 2014 12:09:34 -0700 Subject: [PATCH 06/47] Disable gravity on script start --- examples/swissArmyJetpack.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/swissArmyJetpack.js b/examples/swissArmyJetpack.js index fb4dc1dc04..4cb68e9b64 100644 --- a/examples/swissArmyJetpack.js +++ b/examples/swissArmyJetpack.js @@ -178,6 +178,10 @@ function disableArtificialGravity() { MyAvatar.motionBehaviors = MyAvatar.motionBehaviors & ~AVATAR_MOTION_OBEY_LOCAL_GRAVITY; updateButton(3, false); } +// call this immediately so that avatar doesn't fall before voxel data arrives +// Ideally we would only do this on LOGIN, not when starting the script +// in the middle of a session. +disableArtificialGravity(); function enableArtificialGravity() { // NOTE: setting the gravity automatically sets the AVATAR_MOTION_OBEY_LOCAL_GRAVITY behavior bit. @@ -276,7 +280,6 @@ function update(deltaTime) { } Script.update.connect(update); - // we also handle click detection in our mousePressEvent() function mousePressEvent(event) { var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); From 22bf10a9e7d0e1acead0e52df1ae59d0efe06e32 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 1 May 2014 16:18:55 -0700 Subject: [PATCH 07/47] repair addThrust() API so hydraMove.js works --- interface/src/avatar/MyAvatar.cpp | 72 +++++++++++++++++++++++++------ interface/src/avatar/MyAvatar.h | 9 ++-- 2 files changed, 63 insertions(+), 18 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 2e27706820..2f76032a44 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -47,8 +47,11 @@ const float COLLISION_RADIUS_SCALE = 0.125f; 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 +// to properly follow avatar size. float DEFAULT_MOTOR_TIMESCALE = 0.25f; -float DEFAULT_MAX_MOTOR_SPEED = 300.0f; // TODO: normalize this based on avatar height +float MAX_AVATAR_SPEED = 300.0f; +float MAX_MOTOR_SPEED = MAX_AVATAR_SPEED; MyAvatar::MyAvatar() : Avatar(), @@ -59,13 +62,12 @@ MyAvatar::MyAvatar() : _gravity(0.0f, -1.0f, 0.0f), _distanceToNearestAvatar(std::numeric_limits::max()), _lastCollisionPosition(0, 0, 0), - _speedBrakes(false), + _wasPushing(false), + _isPushing(false), _thrust(0.0f), - _isThrustOn(false), - _thrustMultiplier(1.0f), _motorVelocity(0.0f), _motorTimescale(DEFAULT_MOTOR_TIMESCALE), - _maxMotorSpeed(DEFAULT_MAX_MOTOR_SPEED), + _maxMotorSpeed(MAX_MOTOR_SPEED), _motionBehaviors(AVATAR_MOTION_DEFAULTS), _lastBodyPenetration(0.0f), _lookAtTargetAvatar(), @@ -162,8 +164,7 @@ void MyAvatar::simulate(float deltaTime) { rampMotor(deltaTime); applyMotor(deltaTime); - // zero thrust so we don't pile up thrust from other sources - _thrust = glm::vec3(0.0f); + applyThrust(deltaTime); updateChatCircle(deltaTime); @@ -669,14 +670,47 @@ void MyAvatar::rampMotor(float deltaTime) { } _motorVelocity = motorLength * direction; } - _speedBrakes = false; + _isPushing = true; } else { - // apply brakes - _speedBrakes = true; + // motor opposes motion (wants to be at rest) _motorVelocity = - localVelocity; } } +float MyAvatar::computeMotorTimescale() { + // The timescale of the motor is the approximate time it takes for the motor to + // accomplish its intended velocity. A short timescale makes the motor strong, + // and a long timescale makes it weak. The value of timescale to use depends + // on what the motor is doing: + // + // (1) braking --> short timescale (aggressive motor assertion) + // (2) pushing --> medium timescale (mild motor assertion) + // (3) inactive --> long timescale (gentle friction for low speeds) + // + // TODO: recover extra braking behavior when flying close to nearest avatar + + float MIN_MOTOR_TIMESCALE = 0.125f; + float MAX_MOTOR_TIMESCALE = 0.5f; + float MIN_BRAKE_SPEED = 0.4f; + + float timescale = MAX_MOTOR_TIMESCALE; + float speed = glm::length(_velocity); + bool areThrusting = (glm::length2(_thrust) > EPSILON); + + if (_wasPushing && !(_isPushing || areThrusting) && speed > MIN_BRAKE_SPEED) { + // we don't change _wasPushing for this case --> + // keeps the brakes on until we go below MIN_BRAKE_SPEED + timescale = MIN_MOTOR_TIMESCALE; + } else { + if (_isPushing) { + timescale = _motorTimescale; + } + _wasPushing = _isPushing || areThrusting; + } + _isPushing = false; + return timescale; +} + void MyAvatar::applyMotor(float deltaTime) { if (!( _motionBehaviors & AVATAR_MOTION_MOTOR_ENABLED)) { // nothing to do --> early exit @@ -706,16 +740,26 @@ void MyAvatar::applyMotor(float deltaTime) { } } - float MIN_MOTOR_TIMESCALE = 0.25f; - float timescale = _speedBrakes ? MIN_MOTOR_TIMESCALE : _motorTimescale; + float timescale = computeMotorTimescale(); // simple critical damping float tau = glm::clamp(deltaTime / timescale, 0.0f, 1.0f); _velocity += tau * deltaVelocity; } -/* Keep this code for the short term as reference in case we need to further tune - *the new velocity model to achieve similar movement response. +void MyAvatar::applyThrust(float deltaTime) { + _velocity += _thrust * deltaTime; + float speed = glm::length(_velocity); + // cap the speed that thrust can achieve + if (speed > MAX_AVATAR_SPEED) { + _velocity *= MAX_AVATAR_SPEED / speed; + } + // zero thrust so we don't pile up thrust from other sources + _thrust = glm::vec3(0.0f); +} + +/* Keep this code for the short term as reference in case we need to further tune the new model + * to achieve legacy movement response. void MyAvatar::updateThrust(float deltaTime) { // // Gather thrust information from keyboard and sensors to apply to avatar motion diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index bfafffdc02..b357f7bb6e 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -127,14 +127,13 @@ private: // old motion stuff glm::vec3 _lastCollisionPosition; - bool _speedBrakes; + bool _wasPushing; + bool _isPushing; glm::vec3 _thrust; // final acceleration from outside sources for the current frame - bool _isThrustOn; - float _thrustMultiplier; // new motion stuff glm::vec3 _motorVelocity; // intended velocity of avatar motion - float _motorTimescale; // timescale for avatar motion to kick in + float _motorTimescale; // timescale for avatar motor to achieve its desired velocity float _maxMotorSpeed; quint32 _motionBehaviors; @@ -148,7 +147,9 @@ private: // private methods void updateOrientation(float deltaTime); void rampMotor(float deltaTime); + float computeMotorTimescale(); void applyMotor(float deltaTime); + void applyThrust(float deltaTime); void updateHandMovementAndTouching(float deltaTime); void updateCollisionWithAvatars(float deltaTime); void updateCollisionWithEnvironment(float deltaTime, float radius); From 9ceda573cc9ec94ddd7e51a81c4f598e0ff8ea05 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 2 May 2014 10:39:27 -0700 Subject: [PATCH 08/47] sphere-cube: add the penetration don't subtract it --- libraries/shared/src/ShapeCollider.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/shared/src/ShapeCollider.cpp b/libraries/shared/src/ShapeCollider.cpp index 31d57f14ad..cc4093d2b1 100644 --- a/libraries/shared/src/ShapeCollider.cpp +++ b/libraries/shared/src/ShapeCollider.cpp @@ -15,6 +15,7 @@ #include "GeometryUtil.h" #include "ShapeCollider.h" +#include "StreamUtils.h" // adebug // NOTE: // @@ -591,7 +592,8 @@ bool listList(const ListShape* listA, const ListShape* listB, CollisionList& col } // helper function -bool sphereAACube(const glm::vec3& sphereCenter, float sphereRadius, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions) { +bool sphereAACube(const glm::vec3& sphereCenter, float sphereRadius, const glm::vec3& cubeCenter, + float cubeSide, CollisionList& collisions) { glm::vec3 BA = cubeCenter - sphereCenter; float distance = glm::length(BA); if (distance > EPSILON) { @@ -649,7 +651,7 @@ bool sphereAACube(const glm::vec3& sphereCenter, float sphereRadius, const glm:: // penetration is the projection of surfaceAB on direction collision->_penetration = glm::dot(surfaceAB, direction) * direction; // contactPoint is on surface of A - collision->_contactPoint = sphereCenter - sphereRadius * direction; + collision->_contactPoint = sphereCenter + sphereRadius * direction; return true; } } From d8cb84080c2fa5cf4dbd961ce2699ae46e6e0540 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 2 May 2014 10:43:10 -0700 Subject: [PATCH 09/47] stop avatar integration when standing at rest --- interface/src/avatar/MyAvatar.cpp | 56 +++++++++++++++++++----------- interface/src/avatar/MyAvatar.h | 4 +-- libraries/avatars/src/AvatarData.h | 4 +-- 3 files changed, 39 insertions(+), 25 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 2f76032a44..ff85900065 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -61,7 +61,6 @@ MyAvatar::MyAvatar() : _shouldJump(false), _gravity(0.0f, -1.0f, 0.0f), _distanceToNearestAvatar(std::numeric_limits::max()), - _lastCollisionPosition(0, 0, 0), _wasPushing(false), _isPushing(false), _thrust(0.0f), @@ -70,6 +69,7 @@ MyAvatar::MyAvatar() : _maxMotorSpeed(MAX_MOTOR_SPEED), _motionBehaviors(AVATAR_MOTION_DEFAULTS), _lastBodyPenetration(0.0f), + _lastFloorContactPoint(0.0f), _lookAtTargetAvatar(), _shouldRender(true), _billboardValid(false), @@ -151,28 +151,44 @@ void MyAvatar::simulate(float deltaTime) { // update the movement of the hand and process handshaking with other avatars... updateHandMovementAndTouching(deltaTime); - // apply gravity - // For gravity, always move the avatar by the amount driven by gravity, so that the collision - // routines will detect it and collide every frame when pulled by gravity to a surface - const float MIN_DISTANCE_AFTER_COLLISION_FOR_GRAVITY = 0.02f; - if (glm::length(_position - _lastCollisionPosition) > MIN_DISTANCE_AFTER_COLLISION_FOR_GRAVITY) { - _velocity += _scale * _gravity * (GRAVITY_EARTH * deltaTime); - } - updateOrientation(deltaTime); - rampMotor(deltaTime); - applyMotor(deltaTime); + float keyboardInput = fabsf(_driveKeys[FWD] - _driveKeys[BACK]) + + fabsf(_driveKeys[RIGHT] - _driveKeys[LEFT]) + + fabsf(_driveKeys[UP] - _driveKeys[DOWN]); - applyThrust(deltaTime); + bool standingOnFloor = false; + float gravityLength = glm::length(_gravity); + if (gravityLength > EPSILON) { + const CapsuleShape& boundingShape = _skeletonModel.getBoundingShape(); + glm::vec3 startCap; + boundingShape.getStartPoint(startCap); + glm::vec3 bottomOfBoundingCapsule = startCap + (boundingShape.getRadius() / gravityLength) * _gravity; - updateChatCircle(deltaTime); + float fallThreshold = 2.f * deltaTime * gravityLength; + standingOnFloor = (glm::distance(bottomOfBoundingCapsule, _lastFloorContactPoint) < fallThreshold); + } + + if (keyboardInput > 0.0f || glm::length2(_velocity) > 0.0f || glm::length2(_thrust) > 0.0f || + ! standingOnFloor) { + + _velocity += _scale * _gravity * (GRAVITY_EARTH * deltaTime); + + updateMotorFromKeyboard(deltaTime); + applyMotor(deltaTime); + applyThrust(deltaTime); + + if (glm::length2(_velocity) < EPSILON) { + _velocity = glm::vec3(0.0f); + } else { + _position += _velocity * deltaTime; + } + } // update moving flag based on speed const float MOVING_SPEED_THRESHOLD = 0.01f; _moving = glm::length(_velocity) > MOVING_SPEED_THRESHOLD; - - _position += _velocity * deltaTime; + updateChatCircle(deltaTime); // update avatar skeleton and simulate hand and head getHand()->collideAgainstOurself(); @@ -631,9 +647,9 @@ void MyAvatar::updateOrientation(float deltaTime) { setOrientation(orientation); } -void MyAvatar::rampMotor(float deltaTime) { +void MyAvatar::updateMotorFromKeyboard(float deltaTime) { // Increase motor velocity until its length is equal to _maxMotorSpeed. - if (!(_motionBehaviors & AVATAR_MOTION_MOTOR_RAMP_AND_BRAKES_ENABLED)) { + if (!(_motionBehaviors & AVATAR_MOTION_MOTOR_KEYBOARD_ENABLED)) { // nothing to do return; } @@ -740,9 +756,8 @@ void MyAvatar::applyMotor(float deltaTime) { } } - float timescale = computeMotorTimescale(); - // simple critical damping + float timescale = computeMotorTimescale(); float tau = glm::clamp(deltaTime / timescale, 0.0f, 1.0f); _velocity += tau * deltaVelocity; } @@ -909,7 +924,6 @@ void MyAvatar::updateCollisionWithEnvironment(float deltaTime, float radius) { if (Application::getInstance()->getEnvironment()->findCapsulePenetration( _position - up * (pelvisFloatingHeight - radius), _position + up * (getSkeletonHeight() - pelvisFloatingHeight + radius), radius, penetration)) { - _lastCollisionPosition = _position; updateCollisionSound(penetration, deltaTime, ENVIRONMENT_COLLISION_FREQUENCY); applyHardCollision(penetration, ENVIRONMENT_SURFACE_ELASTICITY, ENVIRONMENT_SURFACE_DAMPING); } @@ -921,7 +935,7 @@ void MyAvatar::updateCollisionWithVoxels(float deltaTime, float radius) { myCollisions.clear(); const CapsuleShape& boundingShape = _skeletonModel.getBoundingShape(); if (Application::getInstance()->getVoxelTree()->findShapeCollisions(&boundingShape, myCollisions)) { - const float VOXEL_ELASTICITY = 0.4f; + const float VOXEL_ELASTICITY = 0.0f; const float VOXEL_DAMPING = 0.0f; for (int i = 0; i < myCollisions.size(); ++i) { CollisionInfo* collision = myCollisions[i]; diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index b357f7bb6e..eed567d030 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -126,7 +126,6 @@ private: float _distanceToNearestAvatar; // How close is the nearest avatar? // old motion stuff - glm::vec3 _lastCollisionPosition; bool _wasPushing; bool _isPushing; glm::vec3 _thrust; // final acceleration from outside sources for the current frame @@ -138,6 +137,7 @@ private: quint32 _motionBehaviors; glm::vec3 _lastBodyPenetration; + glm::vec3 _lastFloorContactPoint; QWeakPointer _lookAtTargetAvatar; glm::vec3 _targetAvatarPosition; bool _shouldRender; @@ -146,7 +146,7 @@ private: // private methods void updateOrientation(float deltaTime); - void rampMotor(float deltaTime); + void updateMotorFromKeyboard(float deltaTime); float computeMotorTimescale(); void applyMotor(float deltaTime); void applyThrust(float deltaTime); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 3c1aa9963d..af0b0c57d6 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -52,7 +52,7 @@ typedef unsigned long long quint64; // avatar motion behaviors const quint32 AVATAR_MOTION_MOTOR_ENABLED = 1U << 0; -const quint32 AVATAR_MOTION_MOTOR_RAMP_AND_BRAKES_ENABLED = 1U << 1; +const quint32 AVATAR_MOTION_MOTOR_KEYBOARD_ENABLED = 1U << 1; const quint32 AVATAR_MOTION_MOTOR_USE_LOCAL_FRAME = 1U << 2; const quint32 AVATAR_MOTION_MOTOR_COLLISION_SURFACE_ONLY = 1U << 3; @@ -61,7 +61,7 @@ const quint32 AVATAR_MOTION_OBEY_LOCAL_GRAVITY = 1U << 5; const quint32 AVATAR_MOTION_DEFAULTS = AVATAR_MOTION_MOTOR_ENABLED | - AVATAR_MOTION_MOTOR_RAMP_AND_BRAKES_ENABLED | + AVATAR_MOTION_MOTOR_KEYBOARD_ENABLED | AVATAR_MOTION_MOTOR_USE_LOCAL_FRAME; // these bits will be expanded as features are exposed From 5ac301a77ebb87a46efcfce9e32247b56562e557 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 2 May 2014 11:17:46 -0700 Subject: [PATCH 10/47] set the _lastFloorContactPoint --- interface/src/avatar/MyAvatar.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index ff85900065..79a87e2609 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -940,6 +940,7 @@ void MyAvatar::updateCollisionWithVoxels(float deltaTime, float radius) { for (int i = 0; i < myCollisions.size(); ++i) { CollisionInfo* collision = myCollisions[i]; applyHardCollision(collision->_penetration, VOXEL_ELASTICITY, VOXEL_DAMPING); + _lastFloorContactPoint = collision->_contactPoint - collision->_penetration; } const float VOXEL_COLLISION_FREQUENCY = 0.5f; updateCollisionSound(myCollisions[0]->_penetration, deltaTime, VOXEL_COLLISION_FREQUENCY); From 0b2f272abee79143a69a07d499465b180f3ce45e Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 2 May 2014 16:26:44 -0700 Subject: [PATCH 11/47] Fix (well, partial fix) for ASCII FBX files exported from Makehuman. --- libraries/fbx/src/FBXReader.cpp | 98 +++++++++++++++++---------------- 1 file changed, 51 insertions(+), 47 deletions(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 9389d0abf8..cf34ad6e9c 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -406,7 +406,7 @@ QVariantHash parseMapping(QIODevice* device) { QVector createVec3Vector(const QVector& doubleVector) { QVector values; - for (const double* it = doubleVector.constData(), *end = it + doubleVector.size(); it != end; ) { + for (const double* it = doubleVector.constData(), *end = it + (doubleVector.size() / 3 * 3); it != end; ) { float x = *it++; float y = *it++; float z = *it++; @@ -417,7 +417,7 @@ QVector createVec3Vector(const QVector& doubleVector) { QVector createVec2Vector(const QVector& doubleVector) { QVector values; - for (const double* it = doubleVector.constData(), *end = it + doubleVector.size(); it != end; ) { + for (const double* it = doubleVector.constData(), *end = it + (doubleVector.size() / 2 * 2); it != end; ) { float s = *it++; float t = *it++; values.append(glm::vec2(s, -t)); @@ -432,58 +432,59 @@ glm::mat4 createMat4(const QVector& doubleVector) { doubleVector.at(12), doubleVector.at(13), doubleVector.at(14), doubleVector.at(15)); } -QVector getIntVector(const QVariantList& properties, int index) { - if (index >= properties.size()) { +QVector getIntVector(const FBXNode& node) { + foreach (const FBXNode& child, node.children) { + if (child.name == "a") { + return getIntVector(child); + } + } + if (node.properties.isEmpty()) { return QVector(); } - QVector vector = properties.at(index).value >(); + QVector vector = node.properties.at(0).value >(); if (!vector.isEmpty()) { return vector; } - for (; index < properties.size(); index++) { - vector.append(properties.at(index).toInt()); + for (int i = 0; i < node.properties.size(); i++) { + vector.append(node.properties.at(i).toInt()); } return vector; } -QVector getLongVector(const QVariantList& properties, int index) { - if (index >= properties.size()) { - return QVector(); +QVector getFloatVector(const FBXNode& node) { + foreach (const FBXNode& child, node.children) { + if (child.name == "a") { + return getFloatVector(child); + } } - QVector vector = properties.at(index).value >(); - if (!vector.isEmpty()) { - return vector; - } - for (; index < properties.size(); index++) { - vector.append(properties.at(index).toLongLong()); - } - return vector; -} - -QVector getFloatVector(const QVariantList& properties, int index) { - if (index >= properties.size()) { + if (node.properties.isEmpty()) { return QVector(); } - QVector vector = properties.at(index).value >(); + QVector vector = node.properties.at(0).value >(); if (!vector.isEmpty()) { return vector; } - for (; index < properties.size(); index++) { - vector.append(properties.at(index).toFloat()); + for (int i; i < node.properties.size(); i++) { + vector.append(node.properties.at(i).toFloat()); } return vector; } -QVector getDoubleVector(const QVariantList& properties, int index) { - if (index >= properties.size()) { +QVector getDoubleVector(const FBXNode& node) { + foreach (const FBXNode& child, node.children) { + if (child.name == "a") { + return getDoubleVector(child); + } + } + if (node.properties.isEmpty()) { return QVector(); } - QVector vector = properties.at(index).value >(); + QVector vector = node.properties.at(0).value >(); if (!vector.isEmpty()) { return vector; } - for (; index < properties.size(); index++) { - vector.append(properties.at(index).toDouble()); + for (int i; i < node.properties.size(); i++) { + vector.append(node.properties.at(i).toDouble()); } return vector; } @@ -697,6 +698,9 @@ public: }; void appendIndex(MeshData& data, QVector& indices, int index) { + if (index >= data.polygonIndices.size()) { + return; + } int vertexIndex = data.polygonIndices.at(index); if (vertexIndex < 0) { vertexIndex = -vertexIndex - 1; @@ -749,19 +753,19 @@ ExtractedMesh extractMesh(const FBXNode& object) { QVector textures; foreach (const FBXNode& child, object.children) { if (child.name == "Vertices") { - data.vertices = createVec3Vector(getDoubleVector(child.properties, 0)); + data.vertices = createVec3Vector(getDoubleVector(child)); } else if (child.name == "PolygonVertexIndex") { - data.polygonIndices = getIntVector(child.properties, 0); + data.polygonIndices = getIntVector(child); } else if (child.name == "LayerElementNormal") { data.normalsByVertex = false; foreach (const FBXNode& subdata, child.children) { if (subdata.name == "Normals") { - data.normals = createVec3Vector(getDoubleVector(subdata.properties, 0)); + data.normals = createVec3Vector(getDoubleVector(subdata)); } else if (subdata.name == "NormalsIndex") { - data.normalIndices = getIntVector(subdata.properties, 0); + data.normalIndices = getIntVector(subdata); } else if (subdata.name == "MappingInformationType" && subdata.properties.at(0) == "ByVertice") { @@ -771,22 +775,22 @@ ExtractedMesh extractMesh(const FBXNode& object) { } else if (child.name == "LayerElementUV" && child.properties.at(0).toInt() == 0) { foreach (const FBXNode& subdata, child.children) { if (subdata.name == "UV") { - data.texCoords = createVec2Vector(getDoubleVector(subdata.properties, 0)); + data.texCoords = createVec2Vector(getDoubleVector(subdata)); } else if (subdata.name == "UVIndex") { - data.texCoordIndices = getIntVector(subdata.properties, 0); + data.texCoordIndices = getIntVector(subdata); } } } else if (child.name == "LayerElementMaterial") { foreach (const FBXNode& subdata, child.children) { if (subdata.name == "Materials") { - materials = getIntVector(subdata.properties, 0); + materials = getIntVector(subdata); } } } else if (child.name == "LayerElementTexture") { foreach (const FBXNode& subdata, child.children) { if (subdata.name == "TextureId") { - textures = getIntVector(subdata.properties, 0); + textures = getIntVector(subdata); } } } @@ -797,7 +801,7 @@ ExtractedMesh extractMesh(const FBXNode& object) { QHash, int> materialTextureParts; for (int beginIndex = 0; beginIndex < data.polygonIndices.size(); polygonIndex++) { int endIndex = beginIndex; - while (data.polygonIndices.at(endIndex++) >= 0); + while (endIndex < data.polygonIndices.size() && data.polygonIndices.at(endIndex++) >= 0); QPair materialTexture((polygonIndex < materials.size()) ? materials.at(polygonIndex) : 0, (polygonIndex < textures.size()) ? textures.at(polygonIndex) : 0); @@ -820,7 +824,7 @@ ExtractedMesh extractMesh(const FBXNode& object) { appendIndex(data, part.triangleIndices, beginIndex); appendIndex(data, part.triangleIndices, nextIndex++); appendIndex(data, part.triangleIndices, nextIndex); - if (data.polygonIndices.at(nextIndex) < 0) { + if (nextIndex >= data.polygonIndices.size() || data.polygonIndices.at(nextIndex) < 0) { break; } } @@ -835,13 +839,13 @@ FBXBlendshape extractBlendshape(const FBXNode& object) { FBXBlendshape blendshape; foreach (const FBXNode& data, object.children) { if (data.name == "Indexes") { - blendshape.indices = getIntVector(data.properties, 0); + blendshape.indices = getIntVector(data); } else if (data.name == "Vertices") { - blendshape.vertices = createVec3Vector(getDoubleVector(data.properties, 0)); + blendshape.vertices = createVec3Vector(getDoubleVector(data)); } else if (data.name == "Normals") { - blendshape.normals = createVec3Vector(getDoubleVector(data.properties, 0)); + blendshape.normals = createVec3Vector(getDoubleVector(data)); } } return blendshape; @@ -1262,13 +1266,13 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) Cluster cluster; foreach (const FBXNode& subobject, object.children) { if (subobject.name == "Indexes") { - cluster.indices = getIntVector(subobject.properties, 0); + cluster.indices = getIntVector(subobject); } else if (subobject.name == "Weights") { - cluster.weights = getDoubleVector(subobject.properties, 0); + cluster.weights = getDoubleVector(subobject); } else if (subobject.name == "TransformLink") { - QVector values = getDoubleVector(subobject.properties, 0); + QVector values = getDoubleVector(subobject); cluster.transformLink = createMat4(values); } } @@ -1290,7 +1294,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) AnimationCurve curve; foreach (const FBXNode& subobject, object.children) { if (subobject.name == "KeyValueFloat") { - curve.values = getFloatVector(subobject.properties, 0); + curve.values = getFloatVector(subobject); } } animationCurves.insert(getID(object.properties), curve); From 6f3b3acc86fd7e2736701e3ae319211ff3f24a8b Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 2 May 2014 16:27:39 -0700 Subject: [PATCH 12/47] Remove pushback entirely for other avatars. --- interface/src/Application.cpp | 36 ----------------------------------- 1 file changed, 36 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 7fc241daf8..4e7db1fd0c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -555,42 +555,6 @@ void Application::paintGL() { _myCamera.setTargetPosition(_myAvatar->getHead()->calculateAverageEyePosition()); _myCamera.setTargetRotation(_myAvatar->getHead()->getCameraOrientation()); - if (Menu::getInstance()->isOptionChecked(MenuOption::CollideWithAvatars)) { - glm::vec3 planeNormal = _myCamera.getTargetRotation() * IDENTITY_FRONT; - const float BASE_PUSHBACK_RADIUS = 0.25f; - float pushbackRadius = _myCamera.getNearClip() + _myAvatar->getScale() * BASE_PUSHBACK_RADIUS; - glm::vec4 plane(planeNormal, -glm::dot(planeNormal, _myCamera.getTargetPosition()) - pushbackRadius); - - // push camera out of any intersecting avatars - foreach (const AvatarSharedPointer& avatarData, _avatarManager.getAvatarHash()) { - Avatar* avatar = static_cast(avatarData.data()); - if (avatar->isMyAvatar()) { - continue; - } - if (glm::distance(avatar->getPosition(), _myCamera.getTargetPosition()) > - avatar->getBoundingRadius() + pushbackRadius) { - continue; - } - float angle = angleBetween(avatar->getPosition() - _myCamera.getTargetPosition(), planeNormal); - if (angle > PI_OVER_TWO) { - continue; - } - float scale = 1.0f - angle / PI_OVER_TWO; - scale = qMin(1.0f, scale * 2.5f); - static CollisionList collisions(64); - collisions.clear(); - if (!avatar->findPlaneCollisions(plane, collisions)) { - continue; - } - for (int i = 0; i < collisions.size(); i++) { - pushback = qMax(pushback, glm::length(collisions.getCollision(i)->_penetration) * scale); - } - } - const float MAX_PUSHBACK = 0.35f; - pushback = qMin(pushback, MAX_PUSHBACK * _myAvatar->getScale()); - const float BASE_PUSHBACK_FOCAL_LENGTH = 0.5f; - pushbackFocalLength = BASE_PUSHBACK_FOCAL_LENGTH * _myAvatar->getScale(); - } } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { _myCamera.setTightness(0.0f); // Camera is directly connected to head without smoothing _myCamera.setTargetPosition(_myAvatar->getUprightHeadPosition()); From 4c49d00683699dda8178d7473e915618a6635835 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 2 May 2014 17:01:07 -0700 Subject: [PATCH 13/47] Scale adjustment for Makehuman models. --- interface/src/ModelUploader.cpp | 2 +- libraries/fbx/src/FBXReader.cpp | 8 +++++++- libraries/fbx/src/FBXReader.h | 1 + 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/interface/src/ModelUploader.cpp b/interface/src/ModelUploader.cpp index 83851ded8e..25be8fa649 100644 --- a/interface/src/ModelUploader.cpp +++ b/interface/src/ModelUploader.cpp @@ -153,7 +153,7 @@ bool ModelUploader::zip() { // mixamo/autodesk defaults if (!mapping.contains(SCALE_FIELD)) { - mapping.insert(SCALE_FIELD, 15.0); + mapping.insert(SCALE_FIELD, geometry.author == "www.makehuman.org" ? 150.0 : 15.0); } QVariantHash joints = mapping.value(JOINT_FIELD).toHash(); if (!joints.contains("jointEyeLeft")) { diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index cf34ad6e9c..385f45e323 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1020,7 +1020,13 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) foreach (const FBXNode& object, child.children) { if (object.name == "SceneInfo") { foreach (const FBXNode& subobject, object.children) { - if (subobject.name == "Properties70") { + if (subobject.name == "MetaData") { + foreach (const FBXNode& subsubobject, subobject.children) { + if (subsubobject.name == "Author") { + geometry.author = subsubobject.properties.at(0).toString(); + } + } + } else if (subobject.name == "Properties70") { foreach (const FBXNode& subsubobject, subobject.children) { if (subsubobject.name == "P" && subsubobject.properties.size() >= 5 && subsubobject.properties.at(0) == "Original|ApplicationName") { diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index af00e35398..a4b04825ef 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -177,6 +177,7 @@ public: class FBXGeometry { public: + QString author; QString applicationName; ///< the name of the application that generated the model QVector joints; From b458cd1297f3bec28adf391d451ebf0152038960 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 2 May 2014 17:29:38 -0700 Subject: [PATCH 14/47] Default joint mappings for Makehuman models. --- interface/src/ModelUploader.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/interface/src/ModelUploader.cpp b/interface/src/ModelUploader.cpp index 25be8fa649..0ffd725716 100644 --- a/interface/src/ModelUploader.cpp +++ b/interface/src/ModelUploader.cpp @@ -157,10 +157,10 @@ bool ModelUploader::zip() { } QVariantHash joints = mapping.value(JOINT_FIELD).toHash(); if (!joints.contains("jointEyeLeft")) { - joints.insert("jointEyeLeft", "LeftEye"); + joints.insert("jointEyeLeft", geometry.jointIndices.contains("EyeLeft") ? "EyeLeft" : "LeftEye"); } if (!joints.contains("jointEyeRight")) { - joints.insert("jointEyeRight", "RightEye"); + joints.insert("jointEyeRight", geometry.jointIndices.contains("EyeRight") ? "EyeRight" : "RightEye"); } if (!joints.contains("jointNeck")) { joints.insert("jointNeck", "Neck"); @@ -172,7 +172,8 @@ bool ModelUploader::zip() { joints.insert("jointLean", "Spine"); } if (!joints.contains("jointHead")) { - joints.insert("jointHead", geometry.applicationName == "mixamo.com" ? "HeadTop_End" : "HeadEnd"); + const char* topName = (geometry.applicationName == "mixamo.com") ? "HeadTop_End" : "HeadEnd"; + joints.insert("jointHead", geometry.jointIndices.contains(topName) ? topName : "Head"); } if (!joints.contains("jointLeftHand")) { joints.insert("jointLeftHand", "LeftHand"); From c08dfc90c3a661024eb78db0faa63397afcedf42 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 2 May 2014 17:46:11 -0700 Subject: [PATCH 15/47] Add a hack to fix normals on Makehuman exports. --- libraries/fbx/src/FBXReader.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 385f45e323..0bf203be02 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -760,6 +760,7 @@ ExtractedMesh extractMesh(const FBXNode& object) { } else if (child.name == "LayerElementNormal") { data.normalsByVertex = false; + bool indexToDirect = false; foreach (const FBXNode& subdata, child.children) { if (subdata.name == "Normals") { data.normals = createVec3Vector(getDoubleVector(subdata)); @@ -767,11 +768,17 @@ ExtractedMesh extractMesh(const FBXNode& object) { } else if (subdata.name == "NormalsIndex") { data.normalIndices = getIntVector(subdata); - } else if (subdata.name == "MappingInformationType" && - subdata.properties.at(0) == "ByVertice") { + } else if (subdata.name == "MappingInformationType" && subdata.properties.at(0) == "ByVertice") { data.normalsByVertex = true; + + } else if (subdata.name == "ReferenceInformationType" && subdata.properties.at(0) == "IndexToDirect") { + indexToDirect = true; } } + if (indexToDirect && data.normalIndices.isEmpty()) { + // hack to work around wacky Makehuman exports + data.normalsByVertex = true; + } } else if (child.name == "LayerElementUV" && child.properties.at(0).toInt() == 0) { foreach (const FBXNode& subdata, child.children) { if (subdata.name == "UV") { From a1eeef3febad3ab69ceb77e06f0db9febbabf7fc Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 2 May 2014 17:52:52 -0700 Subject: [PATCH 16/47] Out-of-bounds check. --- libraries/fbx/src/FBXReader.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 0bf203be02..ea97c2e789 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -715,7 +715,7 @@ void appendIndex(MeshData& data, QVector& indices, int index) { } else { int normalIndex = data.normalIndices.at(data.normalsByVertex ? vertexIndex : index); - if (normalIndex >= 0) { + if (normalIndex >= 0 && normalIndex < data.normals.size()) { normal = data.normals.at(normalIndex); } } @@ -726,7 +726,7 @@ void appendIndex(MeshData& data, QVector& indices, int index) { } } else { int texCoordIndex = data.texCoordIndices.at(index); - if (texCoordIndex >= 0) { + if (texCoordIndex >= 0 && texCoordIndex < data.texCoords.size()) { vertex.texCoord = data.texCoords.at(texCoordIndex); } } From 0c8b3eaf1aa0c092b8d98a53a30193f2f0245e09 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 2 May 2014 17:56:23 -0700 Subject: [PATCH 17/47] Yet another check. --- libraries/fbx/src/FBXReader.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index ea97c2e789..f3c5133eb3 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -705,6 +705,7 @@ void appendIndex(MeshData& data, QVector& indices, int index) { if (vertexIndex < 0) { vertexIndex = -vertexIndex - 1; } + vertexIndex = qMin(vertexIndex, data.vertices.size() - 1); Vertex vertex; vertex.originalIndex = vertexIndex; From 3e8b5c8714cef10f5d5f637d9a4d116b617d2f80 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 2 May 2014 18:03:54 -0700 Subject: [PATCH 18/47] Let's try that again. --- libraries/fbx/src/FBXReader.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index f3c5133eb3..a5538f8c28 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -705,17 +705,17 @@ void appendIndex(MeshData& data, QVector& indices, int index) { if (vertexIndex < 0) { vertexIndex = -vertexIndex - 1; } - vertexIndex = qMin(vertexIndex, data.vertices.size() - 1); - Vertex vertex; vertex.originalIndex = vertexIndex; glm::vec3 normal; - if (data.normalIndices.isEmpty()) { - normal = data.normals.at(data.normalsByVertex ? vertexIndex : index); - - } else { - int normalIndex = data.normalIndices.at(data.normalsByVertex ? vertexIndex : index); + int normalIndex = data.normalsByVertex ? vertexIndex : index; + if (data.normalIndices.isEmpty()) { + if (normalIndex < data.normals.size()) { + normal = data.normals.at(normalIndex); + } + } else if (normalIndex < data.normalIndices.size()) { + normalIndex = data.normalIndices.at(normalIndex); if (normalIndex >= 0 && normalIndex < data.normals.size()) { normal = data.normals.at(normalIndex); } @@ -725,7 +725,7 @@ void appendIndex(MeshData& data, QVector& indices, int index) { if (index < data.texCoords.size()) { vertex.texCoord = data.texCoords.at(index); } - } else { + } else if (index < data.texCoordIndices.size()) { int texCoordIndex = data.texCoordIndices.at(index); if (texCoordIndex >= 0 && texCoordIndex < data.texCoords.size()) { vertex.texCoord = data.texCoords.at(texCoordIndex); From 8ec03979da56e04d6dcc42d77b9ecbade6839228 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 2 May 2014 18:14:17 -0700 Subject: [PATCH 19/47] OK, one more try. --- libraries/fbx/src/FBXReader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index a5538f8c28..be6846d26a 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -738,7 +738,7 @@ void appendIndex(MeshData& data, QVector& indices, int index) { indices.append(newIndex); data.indices.insert(vertex, newIndex); data.extracted.newIndices.insert(vertexIndex, newIndex); - data.extracted.mesh.vertices.append(data.vertices.at(vertexIndex)); + data.extracted.mesh.vertices.append(data.vertices.at(qMin(vertexIndex, data.vertices.size() - 1))); data.extracted.mesh.normals.append(normal); data.extracted.mesh.texCoords.append(vertex.texCoord); From 9959d8d73fd1e10feb644f2fac3568a1be432408 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 2 May 2014 19:06:44 -0700 Subject: [PATCH 20/47] OK, this time for sure. --- libraries/fbx/src/FBXReader.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index be6846d26a..40a917b06d 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -707,6 +707,11 @@ void appendIndex(MeshData& data, QVector& indices, int index) { } Vertex vertex; vertex.originalIndex = vertexIndex; + + glm::vec3 position; + if (vertexIndex < data.vertices.size()) { + position = data.vertices.at(vertexIndex); + } glm::vec3 normal; int normalIndex = data.normalsByVertex ? vertexIndex : index; @@ -738,7 +743,7 @@ void appendIndex(MeshData& data, QVector& indices, int index) { indices.append(newIndex); data.indices.insert(vertex, newIndex); data.extracted.newIndices.insert(vertexIndex, newIndex); - data.extracted.mesh.vertices.append(data.vertices.at(qMin(vertexIndex, data.vertices.size() - 1))); + data.extracted.mesh.vertices.append(position); data.extracted.mesh.normals.append(normal); data.extracted.mesh.texCoords.append(vertex.texCoord); From 9e6b29951d30b37c0c3bed8bc4f1d610f25f3439 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 2 May 2014 19:14:40 -0700 Subject: [PATCH 21/47] Whoops! --- libraries/fbx/src/FBXReader.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 40a917b06d..a21ed2627a 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -464,7 +464,7 @@ QVector getFloatVector(const FBXNode& node) { if (!vector.isEmpty()) { return vector; } - for (int i; i < node.properties.size(); i++) { + for (int i = 0; i < node.properties.size(); i++) { vector.append(node.properties.at(i).toFloat()); } return vector; @@ -483,7 +483,7 @@ QVector getDoubleVector(const FBXNode& node) { if (!vector.isEmpty()) { return vector; } - for (int i; i < node.properties.size(); i++) { + for (int i = 0; i < node.properties.size(); i++) { vector.append(node.properties.at(i).toDouble()); } return vector; From 3a4dfb92f65cd384031afbb1c2df5f1eed891d01 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 2 May 2014 19:37:42 -0700 Subject: [PATCH 22/47] Let's try using the neck parent rotation, rather than the neck, to fix separate heads. --- interface/src/avatar/FaceModel.cpp | 8 ++++---- interface/src/renderer/Model.cpp | 11 +++++++++++ interface/src/renderer/Model.h | 4 ++++ 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/interface/src/avatar/FaceModel.cpp b/interface/src/avatar/FaceModel.cpp index 709a9fc79d..90e596bde5 100644 --- a/interface/src/avatar/FaceModel.cpp +++ b/interface/src/avatar/FaceModel.cpp @@ -29,11 +29,11 @@ void FaceModel::simulate(float deltaTime, bool fullUpdate) { neckPosition = owningAvatar->getPosition(); } setTranslation(neckPosition); - glm::quat neckRotation; - if (!owningAvatar->getSkeletonModel().getNeckRotation(neckRotation)) { - neckRotation = owningAvatar->getOrientation(); + glm::quat neckParentRotation; + if (!owningAvatar->getSkeletonModel().getNeckParentRotation(neckParentRotation)) { + neckParentRotation = owningAvatar->getOrientation(); } - setRotation(neckRotation); + setRotation(neckParentRotation); const float MODEL_SCALE = 0.0006f; setScale(glm::vec3(1.0f, 1.0f, 1.0f) * _owningHead->getScale() * MODEL_SCALE); diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 5c06d2fcf3..a177783955 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -434,6 +434,17 @@ bool Model::getNeckRotation(glm::quat& neckRotation) const { return isActive() && getJointRotation(_geometry->getFBXGeometry().neckJointIndex, neckRotation); } +bool Model::getNeckParentRotation(glm::quat& neckParentRotation) const { + if (!isActive()) { + return false; + } + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + if (geometry.neckJointIndex == -1) { + return false; + } + return getJointRotation(geometry.joints.at(geometry.neckJointIndex).parentIndex, neckParentRotation); +} + bool Model::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const { if (!isActive()) { return false; diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 14589d1464..ae2bcd79b8 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -132,6 +132,10 @@ public: /// \return whether or not the neck was found bool getNeckRotation(glm::quat& neckRotation) const; + /// Returns the rotation of the neck joint's parent. + /// \return whether or not the neck was found + bool getNeckParentRotation(glm::quat& neckRotation) const; + /// Retrieve the positions of up to two eye meshes. /// \return whether or not both eye meshes were found bool getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const; From c8e56a97cc90777265c320fa7e1a2fc8c9817435 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 2 May 2014 21:47:27 -0700 Subject: [PATCH 23/47] fix sphere-cube collisions, with tests --- libraries/shared/src/ShapeCollider.cpp | 125 ++++++++++----- tests/physics/src/ShapeColliderTests.cpp | 191 ++++++++++++++++++----- tests/physics/src/ShapeColliderTests.h | 3 +- 3 files changed, 241 insertions(+), 78 deletions(-) diff --git a/libraries/shared/src/ShapeCollider.cpp b/libraries/shared/src/ShapeCollider.cpp index cc4093d2b1..ceba1b4c5e 100644 --- a/libraries/shared/src/ShapeCollider.cpp +++ b/libraries/shared/src/ShapeCollider.cpp @@ -15,7 +15,6 @@ #include "GeometryUtil.h" #include "ShapeCollider.h" -#include "StreamUtils.h" // adebug // NOTE: // @@ -594,6 +593,95 @@ bool listList(const ListShape* listA, const ListShape* listB, CollisionList& col // helper function bool sphereAACube(const glm::vec3& sphereCenter, float sphereRadius, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions) { + // sphere is A + // cube is B + // BA = B - A = from center of A to center of B + float halfCubeSide = 0.5f * cubeSide; + glm::vec3 BA = cubeCenter - sphereCenter; + float distance = glm::length(BA); + if (distance > EPSILON) { + float maxBA = glm::max(glm::max(glm::abs(BA.x), glm::abs(BA.y)), glm::abs(BA.z)); + if (maxBA > halfCubeSide + sphereRadius) { + // sphere misses cube entirely + return false; + } + CollisionInfo* collision = collisions.getNewCollision(); + if (!collision) { + return false; + } + if (maxBA > halfCubeSide) { + // sphere hits cube but its center is outside cube + + // contactPoint is on surface of sphere + glm::vec3 cubeContact = glm::abs(BA); + glm::vec3 direction = cubeContact - glm::vec3(halfCubeSide); + + if (direction.x < 0.0f) { + direction.x = 0.0f; + } + if (direction.y < 0.0f) { + direction.y = 0.0f; + } + if (direction.z < 0.0f) { + direction.z = 0.0f; + } + + glm::vec3 signs = glm::sign(BA); + direction.x *= signs.x; + direction.y *= signs.y; + direction.z *= signs.z; + direction = glm::normalize(direction); + collision->_contactPoint = sphereCenter + sphereRadius * direction; + + // penetration points from contact point on cube to that on sphere + if (cubeContact.x > halfCubeSide) { + cubeContact.x = halfCubeSide; + } + cubeContact.x *= -signs.x; + if (cubeContact.y > halfCubeSide) { + cubeContact.y = halfCubeSide; + } + cubeContact.y *= -signs.y; + if (cubeContact.z > halfCubeSide) { + cubeContact.z = halfCubeSide; + } + cubeContact.z *= -signs.z; + //collision->_penetration = collision->_contactPoint - cubeCenter + cubeContact; + collision->_penetration = collision->_contactPoint - (cubeCenter + cubeContact); + } else { + // sphere center is inside cube + // --> push out nearest face + glm::vec3 direction; + BA /= distance; + glm::modf(BA, direction); + direction = glm::normalize(direction); + + // penetration is the projection of surfaceAB on direction + collision->_penetration = (halfCubeSide + sphereRadius - distance * glm::dot(BA, direction)) * direction; + // contactPoint is on surface of A + collision->_contactPoint = sphereCenter + sphereRadius * direction; + } + return true; + } else if (sphereRadius + halfCubeSide > distance) { + // NOTE: for cocentric approximation we collide sphere and cube as two spheres which means + // this algorithm will probably be wrong when both sphere and cube are very small (both ~EPSILON) + CollisionInfo* collision = collisions.getNewCollision(); + if (collision) { + // the penetration and contactPoint are undefined, so we pick a penetration direction (-yAxis) + collision->_penetration = (sphereRadius + halfCubeSide) * glm::vec3(0.0f, -1.0f, 0.0f); + // contactPoint is on surface of A + collision->_contactPoint = sphereCenter + collision->_penetration; + return true; + } + } + return false; +} + +// helper function +/* KEEP THIS CODE -- this is how to collide the cube with stark face normals (no rounding). +* We might want to use this code later for sealing boundaries between adjacent voxels. +bool sphereAACube_StarkAngles(const glm::vec3& sphereCenter, float sphereRadius, const glm::vec3& cubeCenter, + float cubeSide, CollisionList& collisions) { glm::vec3 BA = cubeCenter - sphereCenter; float distance = glm::length(BA); if (distance > EPSILON) { @@ -608,45 +696,11 @@ bool sphereAACube(const glm::vec3& sphereCenter, float sphereRadius, const glm:: if (glm::dot(surfaceAB, BA) > 0.f) { CollisionInfo* collision = collisions.getNewCollision(); if (collision) { - /* KEEP THIS CODE -- this is how to collide the cube with stark face normals (no rounding). - * We might want to use this code later for sealing boundaries between adjacent voxels. // penetration is parallel to box side direction BA /= maxBA; glm::vec3 direction; glm::modf(BA, direction); direction = glm::normalize(direction); - */ - - // For rounded normals at edges and corners: - // At this point imagine that sphereCenter touches a "normalized" cube with rounded edges. - // This cube has a sidelength of 2 and its smoothing radius is sphereRadius/maxBA. - // We're going to try to compute the "negative normal" (and hence direction of penetration) - // of this surface. - - float radius = sphereRadius / (distance * maxBA); // normalized radius - float shortLength = maxBA - radius; - glm::vec3 direction = BA; - if (shortLength > 0.0f) { - direction = glm::abs(BA) - glm::vec3(shortLength); - // Set any negative components to zero, and adopt the sign of the original BA component. - // Unfortunately there isn't an easy way to make this fast. - if (direction.x < 0.0f) { - direction.x = 0.f; - } else if (BA.x < 0.f) { - direction.x = -direction.x; - } - if (direction.y < 0.0f) { - direction.y = 0.f; - } else if (BA.y < 0.f) { - direction.y = -direction.y; - } - if (direction.z < 0.0f) { - direction.z = 0.f; - } else if (BA.z < 0.f) { - direction.z = -direction.z; - } - } - direction = glm::normalize(direction); // penetration is the projection of surfaceAB on direction collision->_penetration = glm::dot(surfaceAB, direction) * direction; @@ -669,6 +723,7 @@ bool sphereAACube(const glm::vec3& sphereCenter, float sphereRadius, const glm:: } return false; } +*/ bool sphereAACube(const SphereShape* sphereA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions) { return sphereAACube(sphereA->getPosition(), sphereA->getRadius(), cubeCenter, cubeSide, collisions); diff --git a/tests/physics/src/ShapeColliderTests.cpp b/tests/physics/src/ShapeColliderTests.cpp index 3f952236e2..7b3d956065 100644 --- a/tests/physics/src/ShapeColliderTests.cpp +++ b/tests/physics/src/ShapeColliderTests.cpp @@ -681,58 +681,164 @@ void ShapeColliderTests::capsuleTouchesCapsule() { } } -void ShapeColliderTests::sphereTouchesAACube() { +void ShapeColliderTests::sphereTouchesAACubeFaces() { CollisionList collisions(16); glm::vec3 cubeCenter(1.23f, 4.56f, 7.89f); + float cubeSide = 2.34f; + + float sphereRadius = 1.13f; + glm::vec3 sphereCenter(0.0f); + SphereShape sphere(sphereRadius, sphereCenter); + + QVector axes; + axes.push_back(xAxis); + axes.push_back(-xAxis); + axes.push_back(yAxis); + axes.push_back(-yAxis); + axes.push_back(zAxis); + axes.push_back(-zAxis); + + for (int i = 0; i < axes.size(); ++i) { + glm::vec3 axis = axes[i]; + // outside + { + collisions.clear(); + float overlap = 0.25f; + float sphereOffset = 0.5f * cubeSide + sphereRadius - overlap; + sphereCenter = cubeCenter + sphereOffset * axis; + sphere.setPosition(sphereCenter); + + if (!ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){ + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should collide with cube. axis = " << axis << std::endl; + } + CollisionInfo* collision = collisions[0]; + if (!collision) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: no CollisionInfo. axis = " << axis << std::endl; + } + + glm::vec3 expectedPenetration = - overlap * axis; + if (glm::distance(expectedPenetration, collision->_penetration) > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: penetration = " << collision->_penetration + << " expected " << expectedPenetration + << " axis = " << axis + << std::endl; + } + + glm::vec3 expectedContact = sphereCenter - sphereRadius * axis; + if (glm::distance(expectedContact, collision->_contactPoint) > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: contactaPoint = " << collision->_contactPoint + << " expected " << expectedContact + << " axis = " << axis + << std::endl; + } + } + + // inside + { + collisions.clear(); + float overlap = 1.25f * sphereRadius; + float sphereOffset = 0.5f * cubeSide + sphereRadius - overlap; + sphereCenter = cubeCenter + sphereOffset * axis; + sphere.setPosition(sphereCenter); + + if (!ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){ + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should collide with cube." + << " axis = " << axis + << std::endl; + } + CollisionInfo* collision = collisions[0]; + if (!collision) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: no CollisionInfo on y-axis." + << " axis = " << axis + << std::endl; + } + + glm::vec3 expectedPenetration = - overlap * axis; + if (glm::distance(expectedPenetration, collision->_penetration) > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: penetration = " << collision->_penetration + << " expected " << expectedPenetration + << " axis = " << axis + << std::endl; + } + + glm::vec3 expectedContact = sphereCenter - sphereRadius * axis; + if (glm::distance(expectedContact, collision->_contactPoint) > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: contactaPoint = " << collision->_contactPoint + << " expected " << expectedContact + << " axis = " << axis + << std::endl; + } + } + } +} + +void ShapeColliderTests::sphereTouchesAACubeEdges() { + CollisionList collisions(20); + + glm::vec3 cubeCenter(0.0f, 0.0f, 0.0f); float cubeSide = 2.0f; float sphereRadius = 1.0f; glm::vec3 sphereCenter(0.0f); SphereShape sphere(sphereRadius, sphereCenter); - float sphereOffset = (0.5f * cubeSide + sphereRadius - 0.25f); + QVector axes; + // edges + axes.push_back(glm::vec3(0.0f, 1.0f, 1.0f)); + axes.push_back(glm::vec3(0.0f, 1.0f, -1.0f)); + axes.push_back(glm::vec3(0.0f, -1.0f, 1.0f)); + axes.push_back(glm::vec3(0.0f, -1.0f, -1.0f)); + axes.push_back(glm::vec3(1.0f, 1.0f, 0.0f)); + axes.push_back(glm::vec3(1.0f, -1.0f, 0.0f)); + axes.push_back(glm::vec3(-1.0f, 1.0f, 0.0f)); + axes.push_back(glm::vec3(-1.0f, -1.0f, 0.0f)); + axes.push_back(glm::vec3(1.0f, 0.0f, 1.0f)); + axes.push_back(glm::vec3(1.0f, 0.0f, -1.0f)); + axes.push_back(glm::vec3(-1.0f, 0.0f, 1.0f)); + axes.push_back(glm::vec3(-1.0f, 0.0f, -1.0f)); + // and corners + axes.push_back(glm::vec3(1.0f, 1.0f, 1.0f)); + axes.push_back(glm::vec3(1.0f, 1.0f, -1.0f)); + axes.push_back(glm::vec3(1.0f, -1.0f, 1.0f)); + axes.push_back(glm::vec3(1.0f, -1.0f, -1.0f)); + axes.push_back(glm::vec3(-1.0f, 1.0f, 1.0f)); + axes.push_back(glm::vec3(-1.0f, 1.0f, -1.0f)); + axes.push_back(glm::vec3(-1.0f, -1.0f, 1.0f)); + axes.push_back(glm::vec3(-1.0f, -1.0f, -1.0f)); - // top - sphereCenter = cubeCenter + sphereOffset * yAxis; - sphere.setPosition(sphereCenter); - if (!ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){ - std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should collide with cube" << std::endl; - } + for (int i =0; i < axes.size(); ++i) { + glm::vec3 axis = axes[i]; + float lengthAxis = glm::length(axis); + axis /= lengthAxis; + float overlap = 0.25f; - // bottom - sphereCenter = cubeCenter - sphereOffset * yAxis; - sphere.setPosition(sphereCenter); - if (!ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){ - std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should collide with cube" << std::endl; - } - - // left - sphereCenter = cubeCenter + sphereOffset * xAxis; - sphere.setPosition(sphereCenter); - if (!ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){ - std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should collide with cube" << std::endl; - } - - // right - sphereCenter = cubeCenter - sphereOffset * xAxis; - sphere.setPosition(sphereCenter); - if (!ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){ - std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should collide with cube" << std::endl; - } - - // forward - sphereCenter = cubeCenter + sphereOffset * zAxis; - sphere.setPosition(sphereCenter); - if (!ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){ - std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should collide with cube" << std::endl; - } - - // back - sphereCenter = cubeCenter - sphereOffset * zAxis; - sphere.setPosition(sphereCenter); - if (!ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){ - std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should collide with cube" << std::endl; + sphereCenter = cubeCenter + (lengthAxis * 0.5f * cubeSide + sphereRadius - overlap) * axis; + sphere.setPosition(sphereCenter); + + if (!ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){ + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should collide with cube. axis = " << axis << std::endl; + } + CollisionInfo* collision = collisions[i]; + if (!collision) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: no CollisionInfo. axis = " << axis << std::endl; + } + + glm::vec3 expectedPenetration = - overlap * axis; + if (glm::distance(expectedPenetration, collision->_penetration) > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: penetration = " << collision->_penetration + << " expected " << expectedPenetration + << " axis = " << axis + << std::endl; + } + + glm::vec3 expectedContact = sphereCenter - sphereRadius * axis; + if (glm::distance(expectedContact, collision->_contactPoint) > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: contactaPoint = " << collision->_contactPoint + << " expected " << expectedContact + << " axis = " << axis + << std::endl; + } } } @@ -802,6 +908,7 @@ void ShapeColliderTests::runAllTests() { capsuleMissesCapsule(); capsuleTouchesCapsule(); - sphereTouchesAACube(); + sphereTouchesAACubeFaces(); + sphereTouchesAACubeEdges(); sphereMissesAACube(); } diff --git a/tests/physics/src/ShapeColliderTests.h b/tests/physics/src/ShapeColliderTests.h index a94f5050ff..b51c48a61e 100644 --- a/tests/physics/src/ShapeColliderTests.h +++ b/tests/physics/src/ShapeColliderTests.h @@ -23,7 +23,8 @@ namespace ShapeColliderTests { void capsuleMissesCapsule(); void capsuleTouchesCapsule(); - void sphereTouchesAACube(); + void sphereTouchesAACubeFaces(); + void sphereTouchesAACubeEdges(); void sphereMissesAACube(); void runAllTests(); From 0e3d66caf6124d85e07a9f24553c89b2174217e9 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 2 May 2014 22:27:52 -0700 Subject: [PATCH 24/47] fix incorrect rescale of vec3 --- libraries/shared/src/ShapeCollider.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/shared/src/ShapeCollider.cpp b/libraries/shared/src/ShapeCollider.cpp index ceba1b4c5e..dbfac66254 100644 --- a/libraries/shared/src/ShapeCollider.cpp +++ b/libraries/shared/src/ShapeCollider.cpp @@ -652,7 +652,7 @@ bool sphereAACube(const glm::vec3& sphereCenter, float sphereRadius, const glm:: // sphere center is inside cube // --> push out nearest face glm::vec3 direction; - BA /= distance; + BA /= maxBA; glm::modf(BA, direction); direction = glm::normalize(direction); From 13170ce23f9b0c9dbc74e60a90a2af5161582314 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Sat, 3 May 2014 14:48:32 +0200 Subject: [PATCH 25/47] There are 6 assignment-client types right now --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ab1212f656..75aa55dacd 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ Any target can be terminated with Ctrl-C (SIGINT) in the associated Terminal win This assignment-client will grab one assignment from the domain-server. You can tell the assignment-client what type you want it to be with the `-t` option. You can also run an assignment-client that forks off *n* assignment-clients with the `-n` option. - ./assignment-client -n 5 + ./assignment-client -n 6 To test things out you'll want to run the Interface client. From 2f1e5ad5cac8f375603be4f89d00a4110ef4208b Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Sat, 3 May 2014 14:51:16 +0200 Subject: [PATCH 26/47] description update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 75aa55dacd..d5fa8de1b4 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ you to run the full stack of the virtual world. In order to set up your own virtual world, you need to set up and run your own local "domain". -The domain-server gives a number different types of assignments to the assignment-client for different features: audio, avatars, voxels, particles, and meta-voxels. +The domain-server gives a number different types of assignments to the assignment-client for different features: audio, avatars, voxels, particles, meta-voxels and models. Follow the instructions in the [build guide](BUILD.md) to build the various components. From 6cc8d53a6ddb94ac1404cd081bbd0853bb62fac7 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Sun, 4 May 2014 22:14:05 -0700 Subject: [PATCH 27/47] fix sphere-cube collisions at edges --- tests/physics/src/ShapeColliderTests.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/physics/src/ShapeColliderTests.cpp b/tests/physics/src/ShapeColliderTests.cpp index 7b3d956065..50d8f4bc66 100644 --- a/tests/physics/src/ShapeColliderTests.cpp +++ b/tests/physics/src/ShapeColliderTests.cpp @@ -701,6 +701,7 @@ void ShapeColliderTests::sphereTouchesAACubeFaces() { for (int i = 0; i < axes.size(); ++i) { glm::vec3 axis = axes[i]; + std::cout << "adebug j = " << i << " axis = " << axis << std::endl; // adebug // outside { collisions.clear(); @@ -809,6 +810,7 @@ void ShapeColliderTests::sphereTouchesAACubeEdges() { for (int i =0; i < axes.size(); ++i) { glm::vec3 axis = axes[i]; + std::cout << "adebug i = " << i << " axis = " << axis << std::endl; // adebug float lengthAxis = glm::length(axis); axis /= lengthAxis; float overlap = 0.25f; From 1d6426206720f113dfdb419c2c122c2ea76e947f Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 5 May 2014 08:46:35 -0700 Subject: [PATCH 28/47] whoops, committed wrong file. Rolling back. --- tests/physics/src/ShapeColliderTests.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/physics/src/ShapeColliderTests.cpp b/tests/physics/src/ShapeColliderTests.cpp index 50d8f4bc66..7b3d956065 100644 --- a/tests/physics/src/ShapeColliderTests.cpp +++ b/tests/physics/src/ShapeColliderTests.cpp @@ -701,7 +701,6 @@ void ShapeColliderTests::sphereTouchesAACubeFaces() { for (int i = 0; i < axes.size(); ++i) { glm::vec3 axis = axes[i]; - std::cout << "adebug j = " << i << " axis = " << axis << std::endl; // adebug // outside { collisions.clear(); @@ -810,7 +809,6 @@ void ShapeColliderTests::sphereTouchesAACubeEdges() { for (int i =0; i < axes.size(); ++i) { glm::vec3 axis = axes[i]; - std::cout << "adebug i = " << i << " axis = " << axis << std::endl; // adebug float lengthAxis = glm::length(axis); axis /= lengthAxis; float overlap = 0.25f; From b097eed1aacb0bb903d39b9125a77ccbe8baaf20 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 5 May 2014 08:47:02 -0700 Subject: [PATCH 29/47] fix sphere-cube collisions at edges (really) --- libraries/shared/src/ShapeCollider.cpp | 46 +++++++++++--------------- 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/libraries/shared/src/ShapeCollider.cpp b/libraries/shared/src/ShapeCollider.cpp index dbfac66254..6291f5a740 100644 --- a/libraries/shared/src/ShapeCollider.cpp +++ b/libraries/shared/src/ShapeCollider.cpp @@ -612,42 +612,36 @@ bool sphereAACube(const glm::vec3& sphereCenter, float sphereRadius, const glm:: if (maxBA > halfCubeSide) { // sphere hits cube but its center is outside cube - // contactPoint is on surface of sphere + // compute contact anti-pole on cube (in cube frame) glm::vec3 cubeContact = glm::abs(BA); - glm::vec3 direction = cubeContact - glm::vec3(halfCubeSide); - - if (direction.x < 0.0f) { - direction.x = 0.0f; - } - if (direction.y < 0.0f) { - direction.y = 0.0f; - } - if (direction.z < 0.0f) { - direction.z = 0.0f; - } - - glm::vec3 signs = glm::sign(BA); - direction.x *= signs.x; - direction.y *= signs.y; - direction.z *= signs.z; - direction = glm::normalize(direction); - collision->_contactPoint = sphereCenter + sphereRadius * direction; - - // penetration points from contact point on cube to that on sphere if (cubeContact.x > halfCubeSide) { cubeContact.x = halfCubeSide; } - cubeContact.x *= -signs.x; if (cubeContact.y > halfCubeSide) { cubeContact.y = halfCubeSide; } - cubeContact.y *= -signs.y; if (cubeContact.z > halfCubeSide) { cubeContact.z = halfCubeSide; } - cubeContact.z *= -signs.z; - //collision->_penetration = collision->_contactPoint - cubeCenter + cubeContact; - collision->_penetration = collision->_contactPoint - (cubeCenter + cubeContact); + glm::vec3 signs = glm::sign(BA); + cubeContact.x *= signs.x; + cubeContact.y *= signs.y; + cubeContact.z *= signs.z; + + // compute collision details + glm::vec3 direction = BA - cubeContact; + float lengthDirection = glm::length(direction); + if (lengthDirection < EPSILON) { + // sphereCenter is touching cube surface, so we can't use the difference between those two + // points to compute the penetration direction. Instead we use the unitary components of + // cubeContact. + direction = cubeContact / halfCubeSide; + glm::modf(BA, direction); + lengthDirection = glm::length(direction); + } + direction /= lengthDirection; + collision->_contactPoint = sphereCenter + sphereRadius * direction; + collision->_penetration = sphereRadius * direction - (BA - cubeContact); } else { // sphere center is inside cube // --> push out nearest face From 0cecef6441ca8d381bc9962c14731dbdfc358a35 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 5 May 2014 10:01:31 -0700 Subject: [PATCH 30/47] fix "nearly but not quite" edge/corner collisions --- libraries/shared/src/ShapeCollider.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/libraries/shared/src/ShapeCollider.cpp b/libraries/shared/src/ShapeCollider.cpp index 6291f5a740..97a963fd46 100644 --- a/libraries/shared/src/ShapeCollider.cpp +++ b/libraries/shared/src/ShapeCollider.cpp @@ -628,7 +628,7 @@ bool sphereAACube(const glm::vec3& sphereCenter, float sphereRadius, const glm:: cubeContact.y *= signs.y; cubeContact.z *= signs.z; - // compute collision details + // compute penetration direction glm::vec3 direction = BA - cubeContact; float lengthDirection = glm::length(direction); if (lengthDirection < EPSILON) { @@ -638,8 +638,12 @@ bool sphereAACube(const glm::vec3& sphereCenter, float sphereRadius, const glm:: direction = cubeContact / halfCubeSide; glm::modf(BA, direction); lengthDirection = glm::length(direction); + } else if (lengthDirection > sphereRadius) { + return false; } direction /= lengthDirection; + + // compute collision details collision->_contactPoint = sphereCenter + sphereRadius * direction; collision->_penetration = sphereRadius * direction - (BA - cubeContact); } else { @@ -650,9 +654,8 @@ bool sphereAACube(const glm::vec3& sphereCenter, float sphereRadius, const glm:: glm::modf(BA, direction); direction = glm::normalize(direction); - // penetration is the projection of surfaceAB on direction + // compute collision details collision->_penetration = (halfCubeSide + sphereRadius - distance * glm::dot(BA, direction)) * direction; - // contactPoint is on surface of A collision->_contactPoint = sphereCenter + sphereRadius * direction; } return true; From 7f7cd6f65463b8de3fc917d3981917cd85b2a21a Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 5 May 2014 11:15:55 -0700 Subject: [PATCH 31/47] Fix for OS X Fullscreen (closes #2414) --- interface/src/MainWindow.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/interface/src/MainWindow.cpp b/interface/src/MainWindow.cpp index 7fddbfffa3..3c2f4a7ca4 100644 --- a/interface/src/MainWindow.cpp +++ b/interface/src/MainWindow.cpp @@ -10,6 +10,7 @@ // #include "MainWindow.h" +#include "Menu.h" #include #include @@ -56,6 +57,10 @@ void MainWindow::changeEvent(QEvent* event) { } else { emit windowShown(true); } + + if (isFullScreen() != Menu::getInstance()->isOptionChecked(MenuOption::Fullscreen)) { + Menu::getInstance()->setIsOptionChecked(MenuOption::Fullscreen, isFullScreen()); + } } else if (event->type() == QEvent::ActivationChange) { if (isActiveWindow()) { emit windowShown(true); From 31b5c39f167f8a12f1334633c26245eb2411b2ae Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 5 May 2014 11:17:11 -0700 Subject: [PATCH 32/47] Fix warnings --- interface/src/ui/ScriptLineNumberArea.cpp | 2 +- interface/src/ui/ScriptLineNumberArea.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/ScriptLineNumberArea.cpp b/interface/src/ui/ScriptLineNumberArea.cpp index 9173c72375..5bb08918b9 100644 --- a/interface/src/ui/ScriptLineNumberArea.cpp +++ b/interface/src/ui/ScriptLineNumberArea.cpp @@ -19,7 +19,7 @@ ScriptLineNumberArea::ScriptLineNumberArea(ScriptEditBox* scriptEditBox) : _scriptEditBox = scriptEditBox; } -QSize ScriptLineNumberArea::sizeHint() { +QSize ScriptLineNumberArea::sizeHint() const { return QSize(_scriptEditBox->lineNumberAreaWidth(), 0); } diff --git a/interface/src/ui/ScriptLineNumberArea.h b/interface/src/ui/ScriptLineNumberArea.h index 75be2048f0..47c540ca0e 100644 --- a/interface/src/ui/ScriptLineNumberArea.h +++ b/interface/src/ui/ScriptLineNumberArea.h @@ -19,7 +19,7 @@ class ScriptLineNumberArea : public QWidget { public: ScriptLineNumberArea(ScriptEditBox* scriptEditBox); - QSize sizeHint(); + QSize sizeHint() const; protected: void paintEvent(QPaintEvent* event); From 7855bf8e2434eca94d3a7b3d9c9e0396378d3d71 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 5 May 2014 11:35:50 -0700 Subject: [PATCH 33/47] Fix scale issue in editVoxels.js (closes #2776) --- examples/editVoxels.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/editVoxels.js b/examples/editVoxels.js index 453ac28649..8e85341e83 100644 --- a/examples/editVoxels.js +++ b/examples/editVoxels.js @@ -331,8 +331,9 @@ function ScaleSelector() { }); this.setScale = function(scale) { this.scale = scale; - this.power = Math.floor(Math.log(scale)); + this.power = Math.ceil(Math.log(scale) * Math.LOG2E); rescaleImport(); + this.update(); } this.show = function(doShow) { @@ -835,7 +836,6 @@ function showPreviewLines() { if (copyScale) { scaleSelector.setScale(intersection.voxel.s); - scaleSelector.update(); } moveTools(); } else { From 1ecf0e3b9d3f4b019163cd3ba95254477c8205da Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 5 May 2014 11:43:58 -0700 Subject: [PATCH 34/47] Used wrong formula in previous commit --- examples/editVoxels.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/editVoxels.js b/examples/editVoxels.js index 8e85341e83..b9f5d925d9 100644 --- a/examples/editVoxels.js +++ b/examples/editVoxels.js @@ -331,7 +331,7 @@ function ScaleSelector() { }); this.setScale = function(scale) { this.scale = scale; - this.power = Math.ceil(Math.log(scale) * Math.LOG2E); + this.power = Math.floor(Math.log(scale) / Math.log(2)); rescaleImport(); this.update(); } From d2f858100e371c033dff9369c8440d7e6c9318ce Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 5 May 2014 12:30:38 -0700 Subject: [PATCH 35/47] add CollisionList::deleteLastCollision() --- libraries/shared/src/CollisionInfo.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/shared/src/CollisionInfo.h b/libraries/shared/src/CollisionInfo.h index 510728daa6..f014a31f36 100644 --- a/libraries/shared/src/CollisionInfo.h +++ b/libraries/shared/src/CollisionInfo.h @@ -81,6 +81,9 @@ public: /// \return pointer to next collision. NULL if list is full. CollisionInfo* getNewCollision(); + /// \forget about collision at the end + void deleteLastCollision(); + /// \return pointer to collision by index. NULL if index out of bounds. CollisionInfo* getCollision(int index); From f6b2b433763b30e5e07da651ead8180ae7ae2969 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 5 May 2014 12:31:36 -0700 Subject: [PATCH 36/47] forget collision for this failure case --- libraries/shared/src/ShapeCollider.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/shared/src/ShapeCollider.cpp b/libraries/shared/src/ShapeCollider.cpp index 97a963fd46..348f8ac97d 100644 --- a/libraries/shared/src/ShapeCollider.cpp +++ b/libraries/shared/src/ShapeCollider.cpp @@ -639,6 +639,7 @@ bool sphereAACube(const glm::vec3& sphereCenter, float sphereRadius, const glm:: glm::modf(BA, direction); lengthDirection = glm::length(direction); } else if (lengthDirection > sphereRadius) { + collisions.deleteLastCollision(); return false; } direction /= lengthDirection; From ddc9408a553da717fdd92aad806f154459d628b4 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 5 May 2014 12:32:23 -0700 Subject: [PATCH 37/47] commit implementation of deleteLastCollision() --- libraries/shared/src/CollisionInfo.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libraries/shared/src/CollisionInfo.cpp b/libraries/shared/src/CollisionInfo.cpp index 5d97842530..22929fbd51 100644 --- a/libraries/shared/src/CollisionInfo.cpp +++ b/libraries/shared/src/CollisionInfo.cpp @@ -23,6 +23,12 @@ CollisionInfo* CollisionList::getNewCollision() { return (_size < _maxSize) ? &(_collisions[_size++]) : NULL; } +void CollisionList::deleteLastCollision() { + if (_size > 0) { + --_size; + } +} + CollisionInfo* CollisionList::getCollision(int index) { return (index > -1 && index < _size) ? &(_collisions[index]) : NULL; } From 6b129c86d0439042d409a03339dea6da0b7de225 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 5 May 2014 12:33:05 -0700 Subject: [PATCH 38/47] reduce snagging when walking across voxel seams --- interface/src/avatar/MyAvatar.cpp | 52 ++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 4 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 79a87e2609..8e5e55d0a1 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -937,11 +937,55 @@ void MyAvatar::updateCollisionWithVoxels(float deltaTime, float radius) { if (Application::getInstance()->getVoxelTree()->findShapeCollisions(&boundingShape, myCollisions)) { const float VOXEL_ELASTICITY = 0.0f; const float VOXEL_DAMPING = 0.0f; - for (int i = 0; i < myCollisions.size(); ++i) { - CollisionInfo* collision = myCollisions[i]; - applyHardCollision(collision->_penetration, VOXEL_ELASTICITY, VOXEL_DAMPING); - _lastFloorContactPoint = collision->_contactPoint - collision->_penetration; + + if (glm::length2(_gravity) > EPSILON) { + if (myCollisions.size() == 1) { + // trivial case + CollisionInfo* collision = myCollisions[0]; + applyHardCollision(collision->_penetration, VOXEL_ELASTICITY, VOXEL_DAMPING); + } else { + // This is special collision handling for when walking on a voxel field which + // prevents snagging at corners and seams. + + // sift through the collisions looking for one against the "floor" + int floorIndex = 0; + float distanceToFloor = 0.0f; + float penetrationWithFloor = 0.0f; + for (int i = 0; i < myCollisions.size(); ++i) { + CollisionInfo* collision = myCollisions[i]; + float distance = glm::dot(_gravity, collision->_contactPoint - _position); + if (distance > distanceToFloor) { + distanceToFloor = distance; + penetrationWithFloor = glm::dot(_gravity, collision->_penetration); + floorIndex = i; + } + } + + // step through the collisions again and apply each that is not redundant + glm::vec3 oldPosition = _position; + for (int i = 0; i < myCollisions.size(); ++i) { + CollisionInfo* collision = myCollisions[i]; + if (i == floorIndex) { + applyHardCollision(collision->_penetration, VOXEL_ELASTICITY, VOXEL_DAMPING); + } else { + float distance = glm::dot(_gravity, collision->_contactPoint - oldPosition); + float penetration = glm::dot(_gravity, collision->_penetration); + if (distance - distanceToFloor > penetrationWithFloor || penetration > penetrationWithFloor) { + // resolution of the deepest penetration would not resolve this one + // so we apply the collision + applyHardCollision(collision->_penetration, VOXEL_ELASTICITY, VOXEL_DAMPING); + } + } + } + } + } else { + // no gravity -- apply all collisions + for (int i = 0; i < myCollisions.size(); ++i) { + CollisionInfo* collision = myCollisions[i]; + applyHardCollision(collision->_penetration, VOXEL_ELASTICITY, VOXEL_DAMPING); + } } + const float VOXEL_COLLISION_FREQUENCY = 0.5f; updateCollisionSound(myCollisions[0]->_penetration, deltaTime, VOXEL_COLLISION_FREQUENCY); } From ec65940212ebe56fcaa47cc027beff9256429d32 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 5 May 2014 12:34:56 -0700 Subject: [PATCH 39/47] remove debug #include --- interface/src/avatar/Hand.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index 0f0df8a484..7c586656e0 100644 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -13,7 +13,6 @@ #include #include -#include #include "Application.h" #include "Avatar.h" From 5aabd302fc2e8edc9b7b1246fa44e36108d3ff41 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 5 May 2014 12:35:49 -0700 Subject: [PATCH 40/47] tweaks to placeModelsWithHands --- examples/placeModelsWithHands.js | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/examples/placeModelsWithHands.js b/examples/placeModelsWithHands.js index cffc6bd3c9..a6b59ff5c3 100644 --- a/examples/placeModelsWithHands.js +++ b/examples/placeModelsWithHands.js @@ -53,7 +53,7 @@ var numModels = modelURLs.length; function keyPressEvent(event) { - //print("event.text=" + event.text); + debugPrint("event.text=" + event.text); if (event.text == "ESC") { if (leftRecentlyDeleted) { leftRecentlyDeleted = false; @@ -63,7 +63,7 @@ function keyPressEvent(event) { rightRecentlyDeleted = false; rightModelAlreadyInHand = false; } - } else if (event.text == "DELETE") { + } else if (event.text == "DELETE" || event.text == "BACKSPACE") { if (leftModelAlreadyInHand) { print("want to delete leftHandModel=" + leftHandModel); Models.deleteModel(leftHandModel); @@ -122,6 +122,7 @@ function checkControllerSide(whichSide) { var BUTTON_FWD; var BUTTON_3; var palmPosition; + var palmRotation; var modelAlreadyInHand; var handMessage; @@ -129,12 +130,14 @@ function checkControllerSide(whichSide) { BUTTON_FWD = LEFT_BUTTON_FWD; BUTTON_3 = LEFT_BUTTON_3; palmPosition = Controller.getSpatialControlPosition(LEFT_PALM); + palmRotation = Controller.getSpatialControlRawRotation(LEFT_PALM); modelAlreadyInHand = leftModelAlreadyInHand; handMessage = "LEFT"; } else { BUTTON_FWD = RIGHT_BUTTON_FWD; BUTTON_3 = RIGHT_BUTTON_3; palmPosition = Controller.getSpatialControlPosition(RIGHT_PALM); + palmRotation = Controller.getSpatialControlRawRotation(RIGHT_PALM); modelAlreadyInHand = rightModelAlreadyInHand; handMessage = "RIGHT"; } @@ -149,8 +152,7 @@ function checkControllerSide(whichSide) { if (closestModel.isKnownID) { - //debugPrint - print(handMessage + " HAND- CAUGHT SOMETHING!!"); + debugPrint(handMessage + " HAND- CAUGHT SOMETHING!!"); if (whichSide == LEFT_PALM) { leftModelAlreadyInHand = true; @@ -165,9 +167,10 @@ function checkControllerSide(whichSide) { y: modelPosition.y, z: modelPosition.z }, radius: modelRadius, + modelRotation: palmRotation, }; - print(">>>>>>>>>>>> EDIT MODEL.... modelRadius=" +modelRadius); + debugPrint(">>>>>>>>>>>> EDIT MODEL.... modelRadius=" +modelRadius); Models.editModel(closestModel, properties); @@ -189,10 +192,11 @@ function checkControllerSide(whichSide) { y: modelPosition.y, z: modelPosition.z }, radius: modelRadius, + modelRotation: palmRotation, modelURL: modelURLs[currentModelURL] }; - print("modelRadius=" +modelRadius); + debugPrint("modelRadius=" +modelRadius); newModel = Models.addModel(properties); if (whichSide == LEFT_PALM) { @@ -225,16 +229,16 @@ function checkControllerSide(whichSide) { // If holding the model keep it in the palm if (grabButtonPressed) { - //debugPrint - print(">>>>> " + handMessage + "-MODEL IN HAND, grabbing, hold and move"); + debugPrint(">>>>> " + handMessage + "-MODEL IN HAND, grabbing, hold and move"); var modelPosition = getModelHoldPosition(whichSide); var properties = { position: { x: modelPosition.x, y: modelPosition.y, z: modelPosition.z }, radius: modelRadius, + modelRotation: palmRotation, }; - print(">>>>>>>>>>>> EDIT MODEL.... modelRadius=" +modelRadius); + debugPrint(">>>>>>>>>>>> EDIT MODEL.... modelRadius=" +modelRadius); Models.editModel(handModel, properties); } else { From 815262b8027c8060081fc1cea9fc7340703291d6 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 5 May 2014 13:06:55 -0700 Subject: [PATCH 41/47] update _lastFloorContactPoint --- interface/src/avatar/MyAvatar.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 8e5e55d0a1..46608e9cf3 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -943,6 +943,7 @@ void MyAvatar::updateCollisionWithVoxels(float deltaTime, float radius) { // trivial case CollisionInfo* collision = myCollisions[0]; applyHardCollision(collision->_penetration, VOXEL_ELASTICITY, VOXEL_DAMPING); + _lastFloorContactPoint = collision->_contactPoint - collision->_penetration; } else { // This is special collision handling for when walking on a voxel field which // prevents snagging at corners and seams. @@ -967,6 +968,7 @@ void MyAvatar::updateCollisionWithVoxels(float deltaTime, float radius) { CollisionInfo* collision = myCollisions[i]; if (i == floorIndex) { applyHardCollision(collision->_penetration, VOXEL_ELASTICITY, VOXEL_DAMPING); + _lastFloorContactPoint = collision->_contactPoint - collision->_penetration; } else { float distance = glm::dot(_gravity, collision->_contactPoint - oldPosition); float penetration = glm::dot(_gravity, collision->_penetration); From 0c78449cf7b75dd9781851cf912930e07d0d5c18 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 5 May 2014 13:28:42 -0700 Subject: [PATCH 42/47] reduce movement speed when walking --- interface/src/avatar/MyAvatar.cpp | 29 ++++++++++++++++++----------- interface/src/avatar/MyAvatar.h | 2 +- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 46608e9cf3..5d07e3631b 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -157,7 +157,7 @@ void MyAvatar::simulate(float deltaTime) { fabsf(_driveKeys[RIGHT] - _driveKeys[LEFT]) + fabsf(_driveKeys[UP] - _driveKeys[DOWN]); - bool standingOnFloor = false; + bool walkingOnFloor = false; float gravityLength = glm::length(_gravity); if (gravityLength > EPSILON) { const CapsuleShape& boundingShape = _skeletonModel.getBoundingShape(); @@ -166,18 +166,20 @@ void MyAvatar::simulate(float deltaTime) { glm::vec3 bottomOfBoundingCapsule = startCap + (boundingShape.getRadius() / gravityLength) * _gravity; float fallThreshold = 2.f * deltaTime * gravityLength; - standingOnFloor = (glm::distance(bottomOfBoundingCapsule, _lastFloorContactPoint) < fallThreshold); + walkingOnFloor = (glm::distance(bottomOfBoundingCapsule, _lastFloorContactPoint) < fallThreshold); } if (keyboardInput > 0.0f || glm::length2(_velocity) > 0.0f || glm::length2(_thrust) > 0.0f || - ! standingOnFloor) { - + ! walkingOnFloor) { + // apply gravity _velocity += _scale * _gravity * (GRAVITY_EARTH * deltaTime); - updateMotorFromKeyboard(deltaTime); + // update motor and thrust + updateMotorFromKeyboard(deltaTime, walkingOnFloor); applyMotor(deltaTime); applyThrust(deltaTime); + // update position if (glm::length2(_velocity) < EPSILON) { _velocity = glm::vec3(0.0f); } else { @@ -647,7 +649,7 @@ void MyAvatar::updateOrientation(float deltaTime) { setOrientation(orientation); } -void MyAvatar::updateMotorFromKeyboard(float deltaTime) { +void MyAvatar::updateMotorFromKeyboard(float deltaTime, bool walking) { // Increase motor velocity until its length is equal to _maxMotorSpeed. if (!(_motionBehaviors & AVATAR_MOTION_MOTOR_KEYBOARD_ENABLED)) { // nothing to do @@ -671,18 +673,23 @@ void MyAvatar::updateMotorFromKeyboard(float deltaTime) { // Compute motor magnitude if (directionLength > EPSILON) { direction /= directionLength; - const float MIN_WALKING_SPEED = 2.0f; + // the finalMotorSpeed depends on whether we are walking or not + const float MIN_KEYBOARD_CONTROL_SPEED = 2.0f; + const float MAX_WALKING_SPEED = 4.0f * MIN_KEYBOARD_CONTROL_SPEED; + float finalMaxMotorSpeed = walking ? MAX_WALKING_SPEED : _maxMotorSpeed; + float motorLength = glm::length(_motorVelocity); - if (motorLength < MIN_WALKING_SPEED) { - _motorVelocity = MIN_WALKING_SPEED * direction; + if (motorLength < MIN_KEYBOARD_CONTROL_SPEED) { + // an active keyboard motor should never be slower than this + _motorVelocity = MIN_KEYBOARD_CONTROL_SPEED * direction; } else { float MOTOR_LENGTH_TIMESCALE = 1.5f; float tau = glm::clamp(deltaTime / MOTOR_LENGTH_TIMESCALE, 0.0f, 1.0f); float INCREASE_FACTOR = 2.0f; //_motorVelocity *= 1.0f + tau * INCREASE_FACTOR; motorLength *= 1.0f + tau * INCREASE_FACTOR; - if (motorLength > _maxMotorSpeed) { - motorLength = _maxMotorSpeed; + if (motorLength > finalMaxMotorSpeed) { + motorLength = finalMaxMotorSpeed; } _motorVelocity = motorLength * direction; } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index eed567d030..c1a3acf1d4 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -146,7 +146,7 @@ private: // private methods void updateOrientation(float deltaTime); - void updateMotorFromKeyboard(float deltaTime); + void updateMotorFromKeyboard(float deltaTime, bool walking); float computeMotorTimescale(); void applyMotor(float deltaTime); void applyThrust(float deltaTime); From 301c3c564999bcb39e7cfdc56fe9b751bdaec93b Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 5 May 2014 13:47:11 -0700 Subject: [PATCH 43/47] don't ignore wall collisions when walking also reduced max walk speed --- interface/src/avatar/MyAvatar.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 5d07e3631b..975665fc45 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -675,7 +675,7 @@ void MyAvatar::updateMotorFromKeyboard(float deltaTime, bool walking) { 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 = 4.0f * MIN_KEYBOARD_CONTROL_SPEED; + const float MAX_WALKING_SPEED = 3.0f * MIN_KEYBOARD_CONTROL_SPEED; float finalMaxMotorSpeed = walking ? MAX_WALKING_SPEED : _maxMotorSpeed; float motorLength = glm::length(_motorVelocity); @@ -979,11 +979,11 @@ void MyAvatar::updateCollisionWithVoxels(float deltaTime, float radius) { } else { float distance = glm::dot(_gravity, collision->_contactPoint - oldPosition); float penetration = glm::dot(_gravity, collision->_penetration); - if (distance - distanceToFloor > penetrationWithFloor || penetration > penetrationWithFloor) { + if (fabsf(distance - distanceToFloor) > penetrationWithFloor || penetration > penetrationWithFloor) { // resolution of the deepest penetration would not resolve this one // so we apply the collision applyHardCollision(collision->_penetration, VOXEL_ELASTICITY, VOXEL_DAMPING); - } + } } } } From befe75a04c37aa6c484c81fa326f4d99b7562c65 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 5 May 2014 14:03:14 -0700 Subject: [PATCH 44/47] fix broken audio-mixer stats --- assignment-client/src/audio/AudioMixer.cpp | 2 +- domain-server/src/DomainServer.cpp | 1 + libraries/networking/src/NodeList.cpp | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 5fd9efedd7..63b2083aae 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -369,7 +369,7 @@ void AudioMixer::sendStatsPacket() { statsObject["average_mixes_per_listener"] = 0.0; } -// ThreadedAssignment::addPacketStatsAndSendStatsPacket(statsObject); + ThreadedAssignment::addPacketStatsAndSendStatsPacket(statsObject); _sumListeners = 0; _sumMixes = 0; diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index d9927f3833..fa581290b6 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -796,6 +796,7 @@ void DomainServer::processDatagram(const QByteArray& receivedPacket, const HifiS sendDomainListToNode(checkInNode, senderSockAddr, nodeInterestListFromPacket(receivedPacket, numNodeInfoBytes)); } } else if (requestType == PacketTypeNodeJsonStats) { + qDebug() << "Trying to match a stats packet"; SharedNodePointer matchingNode = nodeList->sendingNodeForPacket(receivedPacket); if (matchingNode) { reinterpret_cast(matchingNode->getLinkedData())->parseJSONStatsPacket(receivedPacket); diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index d5c02f8eed..e3d113a1b5 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -79,7 +79,7 @@ qint64 NodeList::sendStatsToDomainServer(const QJsonObject& statsObject) { statsPacketStream << statsObject.toVariantMap(); - return writeDatagram(statsPacket, _domainHandler.getSockAddr(), QUuid()); + return writeUnverifiedDatagram(statsPacket, _domainHandler.getSockAddr()); } void NodeList::timePingReply(const QByteArray& packet, const SharedNodePointer& sendingNode) { From 14c7e28dc8e62440f0aad1b362e2523a6a6324ee Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 5 May 2014 14:04:09 -0700 Subject: [PATCH 45/47] remove a comment for node stats in DS --- domain-server/src/DomainServer.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index fa581290b6..d9927f3833 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -796,7 +796,6 @@ void DomainServer::processDatagram(const QByteArray& receivedPacket, const HifiS sendDomainListToNode(checkInNode, senderSockAddr, nodeInterestListFromPacket(receivedPacket, numNodeInfoBytes)); } } else if (requestType == PacketTypeNodeJsonStats) { - qDebug() << "Trying to match a stats packet"; SharedNodePointer matchingNode = nodeList->sendingNodeForPacket(receivedPacket); if (matchingNode) { reinterpret_cast(matchingNode->getLinkedData())->parseJSONStatsPacket(receivedPacket); From 39196501f1f0403518dece6a20977ec6025cad45 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 5 May 2014 14:23:50 -0700 Subject: [PATCH 46/47] removed some unhelpful comments --- interface/src/avatar/MyAvatar.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 5ef8425b67..0c02e42eb0 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -125,12 +125,10 @@ private: glm::vec3 _environmentGravity; float _distanceToNearestAvatar; // How close is the nearest avatar? - // old motion stuff bool _wasPushing; bool _isPushing; glm::vec3 _thrust; // final acceleration from outside sources for the current frame - // new motion stuff glm::vec3 _motorVelocity; // intended velocity of avatar motion float _motorTimescale; // timescale for avatar motor to achieve its desired velocity float _maxMotorSpeed; From b10d9e39036a14c253c2534ed9167278694bea6d Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 5 May 2014 14:26:51 -0700 Subject: [PATCH 47/47] add LOD support for model and particle rendering --- interface/src/models/ModelTreeRenderer.cpp | 11 ++++++++++- interface/src/models/ModelTreeRenderer.h | 2 ++ libraries/octree/src/OctreeElement.cpp | 1 + libraries/octree/src/OctreeRenderer.cpp | 11 +++++++---- libraries/octree/src/OctreeRenderer.h | 4 ++++ 5 files changed, 24 insertions(+), 5 deletions(-) diff --git a/interface/src/models/ModelTreeRenderer.cpp b/interface/src/models/ModelTreeRenderer.cpp index 0fa434622e..9546c7d1c4 100644 --- a/interface/src/models/ModelTreeRenderer.cpp +++ b/interface/src/models/ModelTreeRenderer.cpp @@ -12,7 +12,7 @@ #include #include "InterfaceConfig.h" - +#include "Menu.h" #include "ModelTreeRenderer.h" ModelTreeRenderer::ModelTreeRenderer() : @@ -118,6 +118,15 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) } } +float ModelTreeRenderer::getSizeScale() const { + return Menu::getInstance()->getVoxelSizeScale(); +} + +int ModelTreeRenderer::getBoundaryLevelAdjust() const { + return Menu::getInstance()->getBoundaryLevelAdjust(); +} + + void ModelTreeRenderer::processEraseMessage(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode) { static_cast(_tree)->processEraseMessage(dataByteArray, sourceNode); } diff --git a/interface/src/models/ModelTreeRenderer.h b/interface/src/models/ModelTreeRenderer.h index a85c408a7b..5ed4720391 100644 --- a/interface/src/models/ModelTreeRenderer.h +++ b/interface/src/models/ModelTreeRenderer.h @@ -36,6 +36,8 @@ public: virtual PacketType getMyQueryMessageType() const { return PacketTypeModelQuery; } virtual PacketType getExpectedPacketType() const { return PacketTypeModelData; } virtual void renderElement(OctreeElement* element, RenderArgs* args); + virtual float getSizeScale() const; + virtual int getBoundaryLevelAdjust() const; void update(); diff --git a/libraries/octree/src/OctreeElement.cpp b/libraries/octree/src/OctreeElement.cpp index 846cf564c2..d54f7aa94b 100644 --- a/libraries/octree/src/OctreeElement.cpp +++ b/libraries/octree/src/OctreeElement.cpp @@ -1208,6 +1208,7 @@ ViewFrustum::location OctreeElement::inFrustum(const ViewFrustum& viewFrustum) c // By doing this, we don't need to test each child voxel's position vs the LOD boundary bool OctreeElement::calculateShouldRender(const ViewFrustum* viewFrustum, float voxelScaleSize, int boundaryLevelAdjust) const { bool shouldRender = false; + if (hasContent()) { float furthestDistance = furthestDistanceToCamera(*viewFrustum); float childBoundary = boundaryDistanceForRenderLevel(getLevel() + 1 + boundaryLevelAdjust, voxelScaleSize); diff --git a/libraries/octree/src/OctreeRenderer.cpp b/libraries/octree/src/OctreeRenderer.cpp index cde1f0923d..5c5da2250f 100644 --- a/libraries/octree/src/OctreeRenderer.cpp +++ b/libraries/octree/src/OctreeRenderer.cpp @@ -140,19 +140,22 @@ void OctreeRenderer::processDatagram(const QByteArray& dataByteArray, const Shar bool OctreeRenderer::renderOperation(OctreeElement* element, void* extraData) { RenderArgs* args = static_cast(extraData); - //if (true || element->isInView(*args->_viewFrustum)) { if (element->isInView(*args->_viewFrustum)) { if (element->hasContent()) { - args->_renderer->renderElement(element, args); + if (element->calculateShouldRender(args->_viewFrustum, args->_sizeScale, args->_boundaryLevelAdjust)) { + args->_renderer->renderElement(element, args); + } else { + return false; // if we shouldn't render, then we also should stop recursing. + } } - return true; + return true; // continue recursing } // if not in view stop recursing return false; } void OctreeRenderer::render() { - RenderArgs args = { 0, this, _viewFrustum }; + RenderArgs args = { 0, this, _viewFrustum, getSizeScale(), getBoundaryLevelAdjust() }; if (_tree) { _tree->lockForRead(); _tree->recurseTreeWithOperation(renderOperation, &args); diff --git a/libraries/octree/src/OctreeRenderer.h b/libraries/octree/src/OctreeRenderer.h index 0337086831..652f9d0399 100644 --- a/libraries/octree/src/OctreeRenderer.h +++ b/libraries/octree/src/OctreeRenderer.h @@ -31,6 +31,8 @@ public: int _renderedItems; OctreeRenderer* _renderer; ViewFrustum* _viewFrustum; + float _sizeScale; + int _boundaryLevelAdjust; }; @@ -46,6 +48,8 @@ public: virtual PacketType getMyQueryMessageType() const = 0; virtual PacketType getExpectedPacketType() const = 0; virtual void renderElement(OctreeElement* element, RenderArgs* args) = 0; + virtual float getSizeScale() const { return DEFAULT_OCTREE_SIZE_SCALE; } + virtual int getBoundaryLevelAdjust() const { return 0; } virtual void setTree(Octree* newTree);