diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d69770678c..de9759959a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1384,7 +1384,9 @@ Avatar* Application::isLookingAtOtherAvatar(glm::vec3& mouseRayOrigin, glm::vec3 if (node->getLinkedData() != NULL && node->getType() == NODE_TYPE_AGENT) { Avatar* avatar = (Avatar *) node->getLinkedData(); glm::vec3 headPosition = avatar->getHead().getPosition(); - if (rayIntersectsSphere(mouseRayOrigin, mouseRayDirection, headPosition, HEAD_SPHERE_RADIUS * avatar->getScale())) { + float distance; + if (rayIntersectsSphere(mouseRayOrigin, mouseRayDirection, headPosition, + HEAD_SPHERE_RADIUS * avatar->getScale(), distance)) { eyePosition = avatar->getHead().getEyePosition(); _lookatIndicatorScale = avatar->getScale(); _lookatOtherPosition = headPosition; @@ -1722,6 +1724,40 @@ void Application::update(float deltaTime) { _myAvatar.simulate(deltaTime, NULL, Menu::getInstance()->getGyroCameraSensitivity()); } + // no transmitter drive implies transmitter pick + if (!Menu::getInstance()->isOptionChecked(MenuOption::TransmitterDrive) && _myTransmitter.isConnected()) { + _transmitterPickStart = _myAvatar.getSkeleton().joint[AVATAR_JOINT_CHEST].position; + glm::vec3 direction = _myAvatar.getOrientation() * + glm::quat(glm::radians(_myTransmitter.getEstimatedRotation())) * IDENTITY_FRONT; + + // check against voxels, avatars + const float MAX_PICK_DISTANCE = 100.0f; + float minDistance = MAX_PICK_DISTANCE; + VoxelDetail detail; + float distance; + BoxFace face; + if (_voxels.findRayIntersection(_transmitterPickStart, direction, detail, distance, face)) { + minDistance = min(minDistance, distance); + } + for(NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { + node->lock(); + if (node->getLinkedData() != NULL) { + Avatar *avatar = (Avatar*)node->getLinkedData(); + if (!avatar->isInitialized()) { + avatar->init(); + } + if (avatar->findRayIntersection(_transmitterPickStart, direction, distance)) { + minDistance = min(minDistance, distance); + } + } + node->unlock(); + } + _transmitterPickEnd = _transmitterPickStart + direction * minDistance; + + } else { + _transmitterPickStart = _transmitterPickEnd = glm::vec3(); + } + if (!OculusManager::isConnected()) { if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { if (_myCamera.getMode() != CAMERA_MODE_MIRROR) { @@ -2271,6 +2307,27 @@ void Application::displaySide(Camera& whichCamera) { } renderFollowIndicator(); + + // render transmitter pick ray, if non-empty + if (_transmitterPickStart != _transmitterPickEnd) { + Glower glower; + float TRANSMITTER_PICK_COLOR[] = { 1.0f, 1.0f, 0.0f }; + glColor3fv(TRANSMITTER_PICK_COLOR); + glLineWidth(3.0f); + glBegin(GL_LINES); + glVertex3f(_transmitterPickStart.x, _transmitterPickStart.y, _transmitterPickStart.z); + glVertex3f(_transmitterPickEnd.x, _transmitterPickEnd.y, _transmitterPickEnd.z); + glEnd(); + glLineWidth(1.0f); + + glPushMatrix(); + glTranslatef(_transmitterPickEnd.x, _transmitterPickEnd.y, _transmitterPickEnd.z); + + float PICK_END_RADIUS = 0.025f; + glutSolidSphere(PICK_END_RADIUS, 8, 8); + + glPopMatrix(); + } } void Application::displayOverlay() { diff --git a/interface/src/Application.h b/interface/src/Application.h index a88ea3269f..7ad64ca707 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -298,6 +298,9 @@ private: glm::vec3 _lookatOtherPosition; float _lookatIndicatorScale; + glm::vec3 _transmitterPickStart; + glm::vec3 _transmitterPickEnd; + bool _perfStatsOn; // Do we want to display perfStats? ChatEntry _chatEntry; // chat entry field diff --git a/interface/src/Util.cpp b/interface/src/Util.cpp index 743957128c..dc825ca765 100644 --- a/interface/src/Util.cpp +++ b/interface/src/Util.cpp @@ -598,13 +598,35 @@ float loadSetting(QSettings* settings, const char* name, float defaultValue) { return value; } -bool rayIntersectsSphere(glm::vec3& rayStarting, glm::vec3& rayNormalizedDirection, glm::vec3& sphereCenter, double sphereRadius) { - glm::vec3 vecFromRayToSphereCenter = sphereCenter - rayStarting; - double projection = glm::dot(vecFromRayToSphereCenter, rayNormalizedDirection); - double shortestDistance = sqrt(glm::dot(vecFromRayToSphereCenter, vecFromRayToSphereCenter) - projection * projection); - if (shortestDistance <= sphereRadius) { - return true; +bool rayIntersectsSphere(const glm::vec3& rayStarting, const glm::vec3& rayNormalizedDirection, + const glm::vec3& sphereCenter, float sphereRadius, float& distance) { + glm::vec3 relativeOrigin = rayStarting - sphereCenter; + + // compute the b, c terms of the quadratic equation (a is dot(direction, direction), which is one) + float b = 2.0f * glm::dot(rayNormalizedDirection, relativeOrigin); + float c = glm::dot(relativeOrigin, relativeOrigin) - sphereRadius * sphereRadius; + + // compute the radicand of the quadratic. if less than zero, there's no intersection + float radicand = b * b - 4.0f * c; + if (radicand < 0.0f) { + return false; } + + // compute the first solution of the quadratic + float root = sqrtf(radicand); + float firstSolution = -b - root; + if (firstSolution > 0.0f) { + distance = firstSolution / 2.0f; + return true; // origin is outside the sphere + } + + // now try the second solution + float secondSolution = -b + root; + if (secondSolution > 0.0f) { + distance = 0.0f; + return true; // origin is inside the sphere + } + return false; } @@ -615,4 +637,4 @@ bool pointInSphere(glm::vec3& point, glm::vec3& sphereCenter, double sphereRadiu return true; } return false; -} \ No newline at end of file +} diff --git a/interface/src/Util.h b/interface/src/Util.h index c1bb2949fa..2926d5bfc4 100644 --- a/interface/src/Util.h +++ b/interface/src/Util.h @@ -74,7 +74,8 @@ void runTimingTests(); float loadSetting(QSettings* settings, const char* name, float defaultValue); -bool rayIntersectsSphere(glm::vec3& rayStarting, glm::vec3& rayNormalizedDirection, glm::vec3& sphereCenter, double sphereRadius); +bool rayIntersectsSphere(const glm::vec3& rayStarting, const glm::vec3& rayNormalizedDirection, + const glm::vec3& sphereCenter, float sphereRadius, float& distance); bool pointInSphere(glm::vec3& point, glm::vec3& sphereCenter, double sphereRadius); diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 9f2ac37518..4635be7357 100755 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -800,6 +800,21 @@ void Avatar::getBodyBallTransform(AvatarJointID jointID, glm::vec3& position, gl rotation = _bodyBall[jointID].rotation; } +bool Avatar::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { + float minDistance = FLT_MAX; + for (int i = 0; i < NUM_AVATAR_BODY_BALLS; i++) { + float distance; + if (rayIntersectsSphere(origin, direction, _bodyBall[i].position, _bodyBall[i].radius, distance)) { + minDistance = min(minDistance, distance); + } + } + if (minDistance == FLT_MAX) { + return false; + } + distance = minDistance; + return true; +} + int Avatar::parseData(unsigned char* sourceBuffer, int numBytes) { // change in position implies movement glm::vec3 oldPosition = _position; diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 4048f15d0f..b04f9f04b8 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -164,6 +164,13 @@ public: // Get the position/rotation of a single body ball void getBodyBallTransform(AvatarJointID jointID, glm::vec3& position, glm::quat& rotation) const; + /// Checks for an intersection between the described ray and any of the avatar's body balls. + /// \param origin the origin of the ray + /// \param direction the unit direction vector + /// \param[out] distance the variable in which to store the distance to intersection + /// \return whether or not the ray intersected + bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; + virtual int parseData(unsigned char* sourceBuffer, int numBytes); static void renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2, float radius1, float radius2);