diff --git a/interface/resources/images/hifi-interface-tools-v2-pie.svg b/interface/resources/images/hifi-interface-tools-v2-pie.svg new file mode 100644 index 0000000000..31691369a0 --- /dev/null +++ b/interface/resources/images/hifi-interface-tools-v2-pie.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 3d741fbfbb..9d90fee9ed 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2071,6 +2071,10 @@ void Application::init() { _pieMenu.init("./resources/images/hifi-interface-tools-v2-pie.svg", _glWidget->width(), _glWidget->height()); + + _followMode = new QAction(this); + connect(_followMode, SIGNAL(triggered()), this, SLOT(toggleFollowMode())); + _pieMenu.addAction(_followMode); } @@ -2082,7 +2086,7 @@ const float HEAD_SPHERE_RADIUS = 0.07; static uint16_t DEFAULT_NODE_ID_REF = 1; -bool Application::isLookingAtOtherAvatar(glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection, +Avatar* Application::isLookingAtOtherAvatar(glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection, glm::vec3& eyePosition, uint16_t& nodeID = DEFAULT_NODE_ID_REF) { NodeList* nodeList = NodeList::getInstance(); @@ -2095,11 +2099,11 @@ bool Application::isLookingAtOtherAvatar(glm::vec3& mouseRayOrigin, glm::vec3& m _lookatIndicatorScale = avatar->getScale(); _lookatOtherPosition = headPosition; nodeID = avatar->getOwningNode()->getNodeID(); - return true; + return avatar; } } } - return false; + return NULL; } void Application::renderLookatIndicator(glm::vec3 pointOfInterest, Camera& whichCamera) { @@ -3526,6 +3530,22 @@ void Application::goHome() { _myAvatar.setPosition(START_LOCATION); } + +void Application::toggleFollowMode() { + glm::vec3 mouseRayOrigin, mouseRayDirection; + _viewFrustum.computePickRay(_pieMenu.getX() / (float)_glWidget->width(), + _pieMenu.getY() / (float)_glWidget->height(), + mouseRayOrigin, mouseRayDirection); + glm::vec3 eyePositionIgnored; + uint16_t nodeIDIgnored; + Avatar* leadingAvatar = isLookingAtOtherAvatar(mouseRayOrigin, + mouseRayDirection, + eyePositionIgnored, + nodeIDIgnored); + + _myAvatar.follow(leadingAvatar); +} + void Application::resetSensors() { _headMouseX = _mouseX = _glWidget->width() / 2; _headMouseY = _mouseY = _glWidget->height() / 2; diff --git a/interface/src/Application.h b/interface/src/Application.h index be8f0a7e3b..ab15138759 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -187,6 +187,8 @@ private slots: glm::vec2 getScaledScreenPoint(glm::vec2 projectedPoint); void goHome(); + void toggleFollowMode(); + private: static void controlledBroadcastToNodes(unsigned char* broadcastData, size_t dataBytes, @@ -206,7 +208,7 @@ private: void init(); void update(float deltaTime); - bool isLookingAtOtherAvatar(glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection, + Avatar* isLookingAtOtherAvatar(glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection, glm::vec3& eyePosition, uint16_t& nodeID); void renderLookatIndicator(glm::vec3 pointOfInterest, Camera& whichCamera); @@ -291,6 +293,8 @@ private: QAction* _simulateLeapHand; // When there's no Leap, use this to pretend there is one and feed fake hand data QAction* _testRaveGlove; // Test fancy sparkle-rave-glove mode + + QAction* _followMode; BandwidthMeter _bandwidthMeter; BandwidthDialog* _bandwidthDialog; diff --git a/interface/src/PieMenu.h b/interface/src/PieMenu.h index 863a91cb45..f80f94f5d4 100644 --- a/interface/src/PieMenu.h +++ b/interface/src/PieMenu.h @@ -28,6 +28,8 @@ public: void resize(int screenWidth, int screenHeight); bool isDisplayed() const {return _isDisplayed;} + int getX () const {return _x;} + int getY () const {return _y;} void mouseMoveEvent (int x, int y); void mousePressEvent (int x, int y); diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 6807e521b8..b4fef89d94 100755 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -101,7 +101,8 @@ Avatar::Avatar(Node* owningNode) : _lastCollisionPosition(0, 0, 0), _speedBrakes(false), _isThrustOn(false), - _voxels(this) + _voxels(this), + _leadingAvatar(NULL) { // give the pointer to our head to inherited _headData variable from AvatarData _headData = &_head; @@ -404,6 +405,33 @@ void Avatar::updateThrust(float deltaTime, Transmitter * transmitter) { _thrust += _scale * THRUST_JUMP * up; _shouldJump = false; } + + // Add thrusts from leading avatar + if (_leadingAvatar != NULL) { + glm::vec3 toTarget = _leadingAvatar->getPosition() - _position; + + if (.5f < up.x * toTarget.x + up.y * toTarget.y + up.z * toTarget.z) { + _thrust += _scale * THRUST_MAG_UP * deltaTime * up; + } else if (up.x * toTarget.x + up.y * toTarget.y + up.z * toTarget.z < -.5f) { + _thrust -= _scale * THRUST_MAG_UP * deltaTime * up; + } + + if (glm::length(_position - _leadingAvatar->getPosition()) > _scale * _stringLength) { + _thrust += _scale * THRUST_MAG_FWD * deltaTime * front; + } else { + toTarget = _leadingAvatar->getHead().getLookAtPosition() - _position; + getHead().setLookAtPosition(_leadingAvatar->getHead().getLookAtPosition()); + } + + float yawAngle = angleBetween(front, glm::vec3(toTarget.x, 0.f, toTarget.z)); + if (yawAngle < -10.f || 10.f < yawAngle){ + if (right.x * toTarget.x + right.y * toTarget.y + right.z * toTarget.z > 0) { + _bodyYawDelta -= YAW_MAG * deltaTime; + } else { + _bodyYawDelta += YAW_MAG * deltaTime; + } + } + } // Add thrusts from Transmitter if (transmitter) { @@ -447,6 +475,18 @@ void Avatar::updateThrust(float deltaTime, Transmitter * transmitter) { _isThrustOn = (glm::length(_thrust) > EPSILON); } +void Avatar::follow(Avatar* leadingAvatar) { + const float MAX_STRING_LENGTH = 2; + + _leadingAvatar = leadingAvatar; + if (_leadingAvatar != NULL) { + _stringLength = glm::length(_position - _leadingAvatar->getPosition()) / _scale; + if (_stringLength > MAX_STRING_LENGTH) { + _stringLength = MAX_STRING_LENGTH; + } + } +} + void Avatar::simulate(float deltaTime, Transmitter* transmitter) { glm::quat orientation = getOrientation(); @@ -475,6 +515,13 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) { if (isMyAvatar()) { updateThrust(deltaTime, transmitter); } + + // Ajust, scale, thrust and lookAt position when following an other avatar + if (isMyAvatar() && _leadingAvatar && _scale != _leadingAvatar->getScale()) { + float scale = 0.95f * _scale + 0.05f * _leadingAvatar->getScale(); + setScale(scale); + Application::getInstance()->getCamera()->setScale(scale); + } // copy velocity so we can use it later for acceleration glm::vec3 oldVelocity = getVelocity(); @@ -681,7 +728,9 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) { _head.setScale(_scale); _head.setSkinColor(glm::vec3(SKIN_COLOR[0], SKIN_COLOR[1], SKIN_COLOR[2])); _head.simulate(deltaTime, isMyAvatar()); - + + + // use speed and angular velocity to determine walking vs. standing if (_speed + fabs(_bodyYawDelta) > 0.2) { _mode = AVATAR_MODE_WALKING; diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index fc1d7e204d..80d5b463bf 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -114,6 +114,7 @@ public: void reset(); void simulate(float deltaTime, Transmitter* transmitter); void updateThrust(float deltaTime, Transmitter * transmitter); + void follow(Avatar* leadingAvatar); void updateFromGyrosAndOrWebcam(bool gyroLook, const glm::vec3& amplifyAngle, float yawFromTouch, @@ -157,18 +158,16 @@ public: float getElapsedTimeMoving () const { return _elapsedTimeMoving;} float getElapsedTimeSinceCollision() const { return _elapsedTimeSinceCollision;} const glm::vec3& getLastCollisionPosition () const { return _lastCollisionPosition;} - float getAbsoluteHeadYaw () const; - float getAbsoluteHeadPitch () const; - Head& getHead () {return _head; } - Hand& getHand () {return _hand; } - glm::quat getOrientation () const; - glm::quat getWorldAlignedOrientation() const; - - const glm::vec3& getMouseRayOrigin() const { return _mouseRayOrigin; } - const glm::vec3& getMouseRayDirection() const { return _mouseRayDirection; } - - - glm::vec3 getGravity () const { return _gravity; } + float getAbsoluteHeadYaw () const; + float getAbsoluteHeadPitch () const; + Head& getHead () {return _head; } + Hand& getHand () {return _hand; } + glm::quat getOrientation () const; + glm::quat getWorldAlignedOrientation() const; + const glm::vec3& getMouseRayOrigin () const { return _mouseRayOrigin; } + const glm::vec3& getMouseRayDirection () const { return _mouseRayDirection; } + Avatar* getLeadingAvatar () const { return _leadingAvatar; } + glm::vec3 getGravity () const { return _gravity; } glm::vec3 getUprightHeadPosition() const; glm::vec3 getUprightEyeLevelPosition() const; @@ -255,7 +254,10 @@ private: glm::vec3 _lastCollisionPosition; bool _speedBrakes; bool _isThrustOn; - + + Avatar* _leadingAvatar; + float _stringLength; + AvatarVoxelSystem _voxels; // private methods...