diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b4fa7db4a9..635e5c16d7 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1105,9 +1105,24 @@ void Application::mouseMoveEvent(QMouseEvent* event) { } if (activeWindow() == _window) { + int deltaX = event->x() - _mouseX; + int deltaY = event->y() - _mouseY; + _mouseX = event->x(); _mouseY = event->y(); + // orbit behavior + if (_mousePressed && !Menu::getInstance()->isVoxelModeActionChecked()) { + if (_lookatTargetAvatar) { + _myAvatar.orbit(_lookatTargetAvatar->getPosition(), deltaX, deltaY); + return; + } + if (_isHoverVoxel) { + _myAvatar.orbit(glm::vec3(_hoverVoxel.x, _hoverVoxel.y, _hoverVoxel.z) * (float)TREE_SCALE, deltaX, deltaY); + return; + } + } + // detect drag glm::vec3 mouseVoxelPos(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z); if (!_justEditedVoxel && mouseVoxelPos != _lastMouseVoxelPos) { @@ -1151,7 +1166,8 @@ void Application::mousePressEvent(QMouseEvent* event) { } if (!_palette.isActive() && (!_isHoverVoxel || _lookatTargetAvatar)) { - _pieMenu.mousePressEvent(_mouseX, _mouseY); + // disable for now + // _pieMenu.mousePressEvent(_mouseX, _mouseY); } if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelSelectMode) && _pasteMode) { @@ -1952,7 +1968,9 @@ void Application::updateLookatTargetAvatar(const glm::vec3& mouseRayOrigin, cons bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateLookatTargetAvatar()"); - _lookatTargetAvatar = findLookatTargetAvatar(mouseRayOrigin, mouseRayDirection, eyePosition, DEFAULT_NODE_ID_REF); + if (!_mousePressed) { + _lookatTargetAvatar = findLookatTargetAvatar(mouseRayOrigin, mouseRayDirection, eyePosition, DEFAULT_NODE_ID_REF); + } } Avatar* Application::findLookatTargetAvatar(const glm::vec3& mouseRayOrigin, const glm::vec3& mouseRayDirection, @@ -1961,17 +1979,15 @@ Avatar* Application::findLookatTargetAvatar(const glm::vec3& mouseRayOrigin, con NodeList* nodeList = NodeList::getInstance(); for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { if (node->getLinkedData() != NULL && node->getType() == NODE_TYPE_AGENT) { - Avatar* avatar = (Avatar *) node->getLinkedData(); - glm::vec3 headPosition = avatar->getHead().getPosition(); + Avatar* avatar = (Avatar*)node->getLinkedData(); float distance; - if (rayIntersectsSphere(mouseRayOrigin, mouseRayDirection, headPosition, - HEAD_SPHERE_RADIUS * avatar->getHead().getScale(), distance)) { + if (avatar->findRayIntersection(mouseRayOrigin, mouseRayDirection, distance)) { // rescale to compensate for head embiggening eyePosition = (avatar->getHead().calculateAverageEyePosition() - avatar->getHead().getScalePivot()) * (avatar->getScale() / avatar->getHead().getScale()) + avatar->getHead().getScalePivot(); _lookatIndicatorScale = avatar->getHead().getScale(); - _lookatOtherPosition = headPosition; + _lookatOtherPosition = avatar->getHead().getPosition(); nodeUUID = avatar->getOwningNode()->getUUID(); return avatar; } @@ -2213,7 +2229,7 @@ void Application::updateHoverVoxels(float deltaTime, glm::vec3& mouseRayOrigin, glm::vec4 oldVoxel(_hoverVoxel.x, _hoverVoxel.y, _hoverVoxel.z, _hoverVoxel.s); // only do this work if MAKE_SOUND_ON_VOXEL_HOVER or MAKE_SOUND_ON_VOXEL_CLICK is enabled, // and make sure the tree is not already busy... because otherwise you'll have to wait. - if (!_voxels.treeIsBusy()) { + if (!(_voxels.treeIsBusy() || _mousePressed)) { { PerformanceWarning warn(showWarnings, "Application::updateHoverVoxels() _voxels.findRayIntersection()"); _isHoverVoxel = _voxels.findRayIntersection(mouseRayOrigin, mouseRayDirection, _hoverVoxel, distance, face); diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 02141d3252..47af6d586c 100755 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -190,10 +190,6 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) { // update avatar skeleton _skeleton.update(deltaTime, getOrientation(), _position); - - // if this is not my avatar, then hand position comes from transmitted data - _skeleton.joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position = _handPosition; - _hand.simulate(deltaTime, false); _skeletonModel.simulate(deltaTime); _head.setBodyRotation(glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll)); @@ -340,8 +336,24 @@ void Avatar::getSkinColors(glm::vec3& lighter, glm::vec3& darker) { } } +bool Avatar::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { + float minDistance = FLT_MAX; + float modelDistance; + if (_skeletonModel.findRayIntersection(origin, direction, modelDistance)) { + minDistance = qMin(minDistance, modelDistance); + } + if (_head.getFaceModel().findRayIntersection(origin, direction, modelDistance)) { + minDistance = qMin(minDistance, modelDistance); + } + if (minDistance < FLT_MAX) { + distance = minDistance; + return true; + } + return false; +} + bool Avatar::findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, - glm::vec3& penetration, int skeletonSkipIndex) { + glm::vec3& penetration, int skeletonSkipIndex) const { bool didPenetrate = false; glm::vec3 totalPenetration; glm::vec3 skeletonPenetration; diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 39df62f603..c74968de9f 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -160,6 +160,8 @@ public: void getSkinColors(glm::vec3& lighter, glm::vec3& darker); + bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; + /// Checks for penetration between the described sphere and the avatar. /// \param penetratorCenter the center of the penetration test sphere /// \param penetratorRadius the radius of the penetration test sphere @@ -167,7 +169,7 @@ public: /// \param skeletonSkipIndex if not -1, the index of a joint to skip (along with its descendents) in the skeleton model /// \return whether or not the sphere penetrated bool findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, - glm::vec3& penetration, int skeletonSkipIndex = -1); + glm::vec3& penetration, int skeletonSkipIndex = -1) const; virtual int parseData(unsigned char* sourceBuffer, int numBytes); diff --git a/interface/src/avatar/Head.h b/interface/src/avatar/Head.h index 4ac5c7c4f5..55c3dffc50 100644 --- a/interface/src/avatar/Head.h +++ b/interface/src/avatar/Head.h @@ -72,6 +72,7 @@ public: VideoFace& getVideoFace() { return _videoFace; } FaceModel& getFaceModel() { return _faceModel; } + const FaceModel& getFaceModel() const { return _faceModel; } const bool getReturnToCenter() const { return _returnHeadToCenter; } // Do you want head to try to return to center (depends on interface detected) float getAverageLoudness() const { return _averageLoudness; } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 0fa4936af0..91b88e427c 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -542,6 +542,16 @@ void MyAvatar::loadData(QSettings* settings) { settings->endGroup(); } +void MyAvatar::orbit(const glm::vec3& position, int deltaX, int deltaY) { + glm::vec3 vector = getPosition() - position; + glm::quat orientation = getOrientation(); + glm::vec3 up = orientation * IDENTITY_UP; + const float ANGULAR_SCALE = 0.5f; + glm::quat rotation = glm::angleAxis(deltaX * -ANGULAR_SCALE, up); + const float LINEAR_SCALE = 0.01f; + setPosition(position + rotation * vector + up * (deltaY * LINEAR_SCALE * _scale)); + setOrientation(rotation * orientation); +} float MyAvatar::getAbsoluteHeadYaw() const { return glm::yaw(_head.getOrientation()); @@ -757,15 +767,11 @@ void MyAvatar::updateHandMovementAndTouching(float deltaTime, bool enableHandMov glm::vec3 farVector = _mouseRayOrigin + pointDirection * (float)TREE_SCALE - shoulderPosition; const float ARM_RETRACTION = 0.75f; float retractedLength = _skeletonModel.getRightArmLength() * ARM_RETRACTION; - _skeleton.joint[AVATAR_JOINT_RIGHT_FINGERTIPS].position = shoulderPosition + - glm::normalize(farVector) * retractedLength; + setHandPosition(shoulderPosition + glm::normalize(farVector) * retractedLength); pointing = true; } } - //Set right hand position and state to be transmitted, and also tell AvatarTouch about it - setHandPosition(_skeleton.joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position); - if (_mousePressed) { _handState = HAND_STATE_GRASPING; } else if (pointing) { @@ -919,8 +925,9 @@ void MyAvatar::updateChatCircle(float deltaTime) { // remove members whose accumulated circles are too far away to influence us const float CIRCUMFERENCE_PER_MEMBER = 0.5f; const float CIRCLE_INFLUENCE_SCALE = 2.0f; + const float MIN_RADIUS = 0.3f; for (int i = sortedAvatars.size() - 1; i >= 0; i--) { - float radius = (CIRCUMFERENCE_PER_MEMBER * (i + 2)) / PI_TIMES_TWO; + float radius = qMax(MIN_RADIUS, (CIRCUMFERENCE_PER_MEMBER * (i + 2)) / PI_TIMES_TWO); if (glm::distance(_position, sortedAvatars[i].accumulatedCenter) > radius * CIRCLE_INFLUENCE_SCALE) { sortedAvatars.remove(i); } else { @@ -931,7 +938,7 @@ void MyAvatar::updateChatCircle(float deltaTime) { return; } center = sortedAvatars.last().accumulatedCenter; - float radius = (CIRCUMFERENCE_PER_MEMBER * (sortedAvatars.size() + 1)) / PI_TIMES_TWO; + float radius = qMax(MIN_RADIUS, (CIRCUMFERENCE_PER_MEMBER * (sortedAvatars.size() + 1)) / PI_TIMES_TWO); // compute the average up vector glm::vec3 up = getWorldAlignedOrientation() * IDENTITY_UP; diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 508f9ce5d2..99cc360d59 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -69,6 +69,8 @@ public: void addThrust(glm::vec3 newThrust) { _thrust += newThrust; }; glm::vec3 getThrust() { return _thrust; }; + void orbit(const glm::vec3& position, int deltaX, int deltaY); + private: bool _mousePressed; float _bodyPitchDelta; diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index e5c23532d8..e7fb6c7c1d 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -546,6 +546,35 @@ glm::vec4 Model::computeAverageColor() const { return _geometry ? _geometry->computeAverageColor() : glm::vec4(1.0f, 1.0f, 1.0f, 1.0f); } +bool Model::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { + const glm::vec3 relativeOrigin = origin - _translation; + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + float minDistance = FLT_MAX; + float radiusScale = extractUniformScale(_scale); + for (int i = 0; i < _jointStates.size(); i++) { + const FBXJoint& joint = geometry.joints[i]; + glm::vec3 end = extractTranslation(_jointStates[i].transform); + float endRadius = joint.boneRadius * radiusScale; + glm::vec3 start = end; + float startRadius = joint.boneRadius * radiusScale; + if (joint.parentIndex != -1) { + start = extractTranslation(_jointStates[joint.parentIndex].transform); + startRadius = geometry.joints[joint.parentIndex].boneRadius * radiusScale; + } + // for now, use average of start and end radii + float capsuleDistance; + if (findRayCapsuleIntersection(relativeOrigin, direction, start, end, + (startRadius + endRadius) / 2.0f, capsuleDistance)) { + minDistance = qMin(minDistance, capsuleDistance); + } + } + if (minDistance < FLT_MAX) { + distance = minDistance; + return true; + } + return false; +} + bool Model::findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, glm::vec3& penetration, float boneScale, int skipIndex) const { const glm::vec3 relativeCenter = penetratorCenter - _translation; diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 2a97345db0..3b1f66938b 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -125,6 +125,8 @@ public: /// Returns the average color of all meshes in the geometry. glm::vec4 computeAverageColor() const; + bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; + bool findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, glm::vec3& penetration, float boneScale = 1.0f, int skipIndex = -1) const; diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 1dd5dacafe..b76d889526 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -115,7 +115,7 @@ public: /// \param skeletonSkipIndex if not -1, the index of a joint to skip (along with its descendents) in the skeleton model /// \return whether or not the sphere penetrated virtual bool findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, - glm::vec3& penetration, int skeletonSkipIndex = -1) { return false; } + glm::vec3& penetration, int skeletonSkipIndex = -1) const { return false; } protected: QUuid _uuid; diff --git a/libraries/shared/src/GeometryUtil.cpp b/libraries/shared/src/GeometryUtil.cpp index 1da05f9fae..58d6e0476e 100644 --- a/libraries/shared/src/GeometryUtil.cpp +++ b/libraries/shared/src/GeometryUtil.cpp @@ -154,6 +154,77 @@ glm::vec3 addPenetrations(const glm::vec3& currentPenetration, const glm::vec3& newPenetration - (currentDirection * directionalComponent); } +bool findRaySphereIntersection(const glm::vec3& origin, const glm::vec3& direction, + const glm::vec3& center, float radius, float& distance) { + glm::vec3 relativeOrigin = origin - center; + float c = glm::dot(relativeOrigin, relativeOrigin) - radius * radius; + if (c < 0.0f) { + distance = 0.0f; + return true; // starts inside the sphere + } + float b = glm::dot(direction, relativeOrigin); + float radicand = b * b - c; + if (radicand < 0.0f) { + return false; // doesn't hit the sphere + } + float t = -b - sqrtf(radicand); + if (t < 0.0f) { + return false; // doesn't hit the sphere + } + distance = t; + return true; +} + +bool findRayCapsuleIntersection(const glm::vec3& origin, const glm::vec3& direction, + const glm::vec3& start, const glm::vec3& end, float radius, float& distance) { + if (start == end) { + return findRaySphereIntersection(origin, direction, start, radius, distance); // handle degenerate case + } + glm::vec3 relativeOrigin = origin - start; + glm::vec3 relativeEnd = end - start; + float capsuleLength = glm::length(relativeEnd); + relativeEnd /= capsuleLength; + float originProjection = glm::dot(relativeEnd, relativeOrigin); + glm::vec3 constant = relativeOrigin - relativeEnd * originProjection; + float c = glm::dot(constant, constant) - radius * radius; + if (c < 0.0f) { // starts inside cylinder + if (originProjection < 0.0f) { // below start + return findRaySphereIntersection(origin, direction, start, radius, distance); + + } else if (originProjection > capsuleLength) { // above end + return findRaySphereIntersection(origin, direction, end, radius, distance); + + } else { // between start and end + distance = 0.0f; + return true; + } + } + glm::vec3 coefficient = direction - relativeEnd * glm::dot(relativeEnd, direction); + float a = glm::dot(coefficient, coefficient); + if (a == 0.0f) { + return false; // parallel to enclosing cylinder + } + float b = 2.0f * glm::dot(constant, coefficient); + float radicand = b * b - 4.0f * a * c; + if (radicand < 0.0f) { + return false; // doesn't hit the enclosing cylinder + } + float t = (-b - sqrtf(radicand)) / (2.0f * a); + if (t < 0.0f) { + return false; // doesn't hit the enclosing cylinder + } + glm::vec3 intersection = relativeOrigin + direction * t; + float intersectionProjection = glm::dot(relativeEnd, intersection); + if (intersectionProjection < 0.0f) { // below start + return findRaySphereIntersection(origin, direction, start, radius, distance); + + } else if (intersectionProjection > capsuleLength) { // above end + return findRaySphereIntersection(origin, direction, end, radius, distance); + } + distance = t; // between start and end + return true; +} + // Do line segments (r1p1.x, r1p1.y)--(r1p2.x, r1p2.y) and (r2p1.x, r2p1.y)--(r2p2.x, r2p2.y) intersect? // from: http://ptspts.blogspot.com/2010/06/how-to-determine-if-two-line-segments.html bool doLineSegmentsIntersect(glm::vec2 r1p1, glm::vec2 r1p2, glm::vec2 r2p1, glm::vec2 r2p2) { diff --git a/libraries/shared/src/GeometryUtil.h b/libraries/shared/src/GeometryUtil.h index 8cbb29580a..8044039e55 100644 --- a/libraries/shared/src/GeometryUtil.h +++ b/libraries/shared/src/GeometryUtil.h @@ -49,6 +49,12 @@ bool findCapsulePlanePenetration(const glm::vec3& penetratorStart, const glm::ve glm::vec3 addPenetrations(const glm::vec3& currentPenetration, const glm::vec3& newPenetration); +bool findRaySphereIntersection(const glm::vec3& origin, const glm::vec3& direction, + const glm::vec3& center, float radius, float& distance); + +bool findRayCapsuleIntersection(const glm::vec3& origin, const glm::vec3& direction, + const glm::vec3& start, const glm::vec3& end, float radius, float& distance); + bool doLineSegmentsIntersect(glm::vec2 r1p1, glm::vec2 r1p2, glm::vec2 r2p1, glm::vec2 r2p2); bool isOnSegment(float xi, float yi, float xj, float yj, float xk, float yk); int computeDirection(float xi, float yi, float xj, float yj, float xk, float yk);