diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index 1f9caf747a..3f5cad655d 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -597,18 +597,11 @@ Item { // Function body by Howard Stearns 2017-01-08 function goToUserInDomain(avatarUuid) { var avatar = AvatarList.getAvatar(avatarUuid); - if (!avatar) { + if (!avatar || !avatar.position || !avatar.orientation) { console.log("This avatar is no longer present. goToUserInDomain() failed."); return; } - // FIXME: We would like the avatar to recompute the avatar's "maybe fly" test at the new position, so that if high enough up, - // the avatar goes into fly mode rather than falling. However, that is not exposed to Javascript right now. - // FIXME: it would be nice if this used the same teleport steps and smoothing as in the teleport.js script. - // Note, however, that this script allows teleporting to a person in the air, while teleport.js is going to a grounded target. - // Position avatar 2 metres from the target in the direction that target avatar was facing. - MyAvatar.position = Vec3.sum(avatar.position, Vec3.multiplyQbyV(avatar.orientation, {x: 0, y: 0, z: -2})); - - // Rotate avatar on Y axis to face target avatar and cancel out any inherited roll and pitch. - MyAvatar.orientation = Quat.cancelOutRollAndPitch(Quat.multiply(avatar.orientation, {y: 1})); + // This is the last step of what AddressManager.goToUser does, but we don't need to resolve the username. + MyAvatar.goToLocation(avatar.position, true, Quat.cancelOutRollAndPitch(avatar.orientation), true); } } diff --git a/interface/resources/qml/hifi/commerce/wallet/NeedsLogIn.qml b/interface/resources/qml/hifi/commerce/wallet/NeedsLogIn.qml index 404d7e84cf..f6875bb06f 100644 --- a/interface/resources/qml/hifi/commerce/wallet/NeedsLogIn.qml +++ b/interface/resources/qml/hifi/commerce/wallet/NeedsLogIn.qml @@ -53,7 +53,7 @@ Item { // Title Bar text RalewaySemiBold { - text: "HIFI COMMERCE - LOGIN"; + text: "Log in to continue"; // Text size size: hifi.fontSizes.overlayTitle; // Anchors diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 02a1959a95..68e417ba1d 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -424,6 +424,7 @@ void MyAvatar::update(float deltaTime) { emit positionGoneTo(); // Run safety tests as soon as we can after goToLocation, or clear if we're not colliding. _physicsSafetyPending = getCollisionsEnabled(); + _characterController.recomputeFlying(); // In case we've gone to into the sky. } if (_physicsSafetyPending && qApp->isPhysicsEnabled() && _characterController.isEnabledAndReady()) { // When needed and ready, arrange to check and fix. @@ -2315,6 +2316,19 @@ void MyAvatar::goToLocation(const glm::vec3& newPosition, bool hasOrientation, const glm::quat& newOrientation, bool shouldFaceLocation) { + // Most cases of going to a place or user go through this now. Some possible improvements to think about in the future: + // - It would be nice if this used the same teleport steps and smoothing as in the teleport.js script, as long as it + // still worked if the target is in the air. + // - Sometimes (such as the response from /api/v1/users/:username/location), the location can be stale, but there is a + // node_id supplied by which we could update the information after going to the stale location first and "looking around". + // This could be passed through AddressManager::goToAddressFromObject => AddressManager::handleViewpoint => here. + // The trick is that you have to yield enough time to resolve the node_id. + // - Instead of always doing the same thing for shouldFaceLocation -- which places users uncomfortabley on top of each other -- + // it would be nice to see how many users are already "at" a person or place, and place ourself in semicircle or other shape + // around the target. Avatars and entities (specified by the node_id) could define an adjustable "face me" method that would + // compute the position (e.g., so that if I'm on stage, going to me would compute an available seat in the audience rather than + // being in my face on-stage). Note that this could work for going to an entity as well as to a person. + qCDebug(interfaceapp).nospace() << "MyAvatar goToLocation - moving to " << newPosition.x << ", " << newPosition.y << ", " << newPosition.z; @@ -3152,6 +3166,7 @@ glm::mat4 MyAvatar::getLeftHandCalibrationMat() const { } bool MyAvatar::pinJoint(int index, const glm::vec3& position, const glm::quat& orientation) { + std::lock_guard guard(_pinnedJointsMutex); auto hipsIndex = getJointIndex("Hips"); if (index != hipsIndex) { qWarning() << "Pinning is only supported for the hips joint at the moment."; @@ -3171,7 +3186,14 @@ bool MyAvatar::pinJoint(int index, const glm::vec3& position, const glm::quat& o return true; } +bool MyAvatar::isJointPinned(int index) { + std::lock_guard guard(_pinnedJointsMutex); + auto it = std::find(_pinnedJoints.begin(), _pinnedJoints.end(), index); + return it != _pinnedJoints.end(); +} + bool MyAvatar::clearPinOnJoint(int index) { + std::lock_guard guard(_pinnedJointsMutex); auto it = std::find(_pinnedJoints.begin(), _pinnedJoints.end(), index); if (it != _pinnedJoints.end()) { _pinnedJoints.erase(it); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 7c9513cb3e..ab74460d4e 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -448,9 +448,8 @@ public: virtual void clearJointData(const QString& name) override; virtual void clearJointsData() override; - - Q_INVOKABLE bool pinJoint(int index, const glm::vec3& position, const glm::quat& orientation); + bool isJointPinned(int index); Q_INVOKABLE bool clearPinOnJoint(int index); Q_INVOKABLE float getIKErrorOnLastSolve() const; @@ -837,6 +836,7 @@ private: bool getIsAway() const { return _isAway; } void setAway(bool value); + std::mutex _pinnedJointsMutex; std::vector _pinnedJoints; // height of user in sensor space, when standing erect. diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index f249be33ea..50e0474831 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -34,12 +34,25 @@ Rig::CharacterControllerState convertCharacterControllerState(CharacterControlle } static AnimPose computeHipsInSensorFrame(MyAvatar* myAvatar, bool isFlying) { + + glm::mat4 worldToSensorMat = glm::inverse(myAvatar->getSensorToWorldMatrix()); + + // check for pinned hips. + auto hipsIndex = myAvatar->getJointIndex("Hips"); + if (myAvatar->isJointPinned(hipsIndex)) { + Transform avatarTransform = myAvatar->getTransform(); + AnimPose result = AnimPose(worldToSensorMat * avatarTransform.getMatrix() * Matrices::Y_180); + result.scale() = glm::vec3(1.0f, 1.0f, 1.0f); + return result; + } else { + DebugDraw::getInstance().removeMarker("pinnedHips"); + } + glm::mat4 hipsMat = myAvatar->deriveBodyFromHMDSensor(); glm::vec3 hipsPos = extractTranslation(hipsMat); glm::quat hipsRot = glmExtractRotation(hipsMat); glm::mat4 avatarToWorldMat = myAvatar->getTransform().getMatrix(); - glm::mat4 worldToSensorMat = glm::inverse(myAvatar->getSensorToWorldMatrix()); glm::mat4 avatarToSensorMat = worldToSensorMat * avatarToWorldMat; // dampen hips rotation, by mixing it with the avatar orientation in sensor space diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index b884dcba17..21815e065a 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -596,7 +596,7 @@ bool AddressManager::handleDomainID(const QString& host) { void AddressManager::handlePath(const QString& path, LookupTrigger trigger, bool wasPathOnly) { if (!handleViewpoint(path, false, trigger, wasPathOnly)) { qCDebug(networking) << "User entered path could not be handled as a viewpoint - " << path << - "- wll attempt to ask domain-server to resolve."; + "- will attempt to ask domain-server to resolve."; if (!wasPathOnly) { // if we received a path with a host then we need to remember what it was here so we can not diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index 32e764bd10..d39930ab76 100755 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -391,6 +391,10 @@ void CharacterController::setState(State desiredState) { } } +void CharacterController::recomputeFlying() { + _pendingFlags |= PENDING_FLAG_RECOMPUTE_FLYING; +} + void CharacterController::setLocalBoundingBox(const glm::vec3& minCorner, const glm::vec3& scale) { float x = scale.x; float z = scale.z; @@ -657,6 +661,13 @@ void CharacterController::updateState() { if (!_dynamicsWorld) { return; } + if (_pendingFlags & PENDING_FLAG_RECOMPUTE_FLYING) { + SET_STATE(CharacterController::State::Hover, "recomputeFlying"); + _hasSupport = false; + _stepHeight = _minStepHeight; // clears memory of last step obstacle + _pendingFlags &= ~PENDING_FLAG_RECOMPUTE_FLYING; + } + const btScalar FLY_TO_GROUND_THRESHOLD = 0.1f * _radius; const btScalar GROUND_TO_FLY_THRESHOLD = 0.8f * _radius + _halfHeight; const quint64 TAKE_OFF_TO_IN_AIR_PERIOD = 250 * MSECS_PER_SECOND; diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h index 0f97cc7c16..96e479dcad 100644 --- a/libraries/physics/src/CharacterController.h +++ b/libraries/physics/src/CharacterController.h @@ -31,6 +31,7 @@ const uint32_t PENDING_FLAG_REMOVE_FROM_SIMULATION = 1U << 1; const uint32_t PENDING_FLAG_UPDATE_SHAPE = 1U << 2; const uint32_t PENDING_FLAG_JUMP = 1U << 3; const uint32_t PENDING_FLAG_UPDATE_COLLISION_GROUP = 1U << 4; +const uint32_t PENDING_FLAG_RECOMPUTE_FLYING = 1U << 5; const float DEFAULT_MIN_FLOOR_NORMAL_DOT_UP = cosf(PI / 3.0f); class btRigidBody; @@ -54,6 +55,7 @@ public: void setGravity(float gravity); float getGravity(); + void recomputeFlying(); virtual void updateShapeIfNecessary() = 0;