From 8f304d95b39aeda6e2e20d6a5b1dfc0eea7caba9 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 25 Feb 2016 15:26:42 -0800 Subject: [PATCH 01/21] rather than computing a velocity for entities held by others as a way to keep it active in local bullet, just call activateBody over and over --- interface/src/avatar/AvatarActionHold.cpp | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 5b493c4215..03842ca5f9 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -153,32 +153,10 @@ std::shared_ptr AvatarActionHold::getTarget(float deltaTimeStep, glm::qu palmPosition = holdingAvatar->getLeftPalmPosition(); palmRotation = holdingAvatar->getLeftPalmRotation(); } - - // In this case we are simulating the grab of another avatar. - // Because the hand controller velocity for their palms is not transmitted over the - // network, we have to synthesize our own. - - if (_previousSet) { - // smooth linear velocity over two frames - glm::vec3 positionalDelta = palmPosition - _previousPositionalTarget; - linearVelocity = (positionalDelta + _previousPositionalDelta) / (deltaTimeStep + _previousDeltaTimeStep); - glm::quat deltaRotation = palmRotation * glm::inverse(_previousRotationalTarget); - float rotationAngle = glm::angle(deltaRotation); - if (rotationAngle > EPSILON) { - angularVelocity = glm::normalize(glm::axis(deltaRotation)); - angularVelocity *= (rotationAngle / deltaTimeStep); - } - - _previousPositionalDelta = positionalDelta; - _previousDeltaTimeStep = deltaTimeStep; - } } rotation = palmRotation * _relativeRotation; position = palmPosition + rotation * _relativePosition; - - // update linearVelocity based on offset via _relativePosition; - linearVelocity = linearVelocity + glm::cross(angularVelocity, position - palmPosition); }); return holdingAvatar; @@ -278,6 +256,7 @@ void AvatarActionHold::doKinematicUpdate(float deltaTimeStep) { }); forceBodyNonStatic(); + activateBody(true); } bool AvatarActionHold::updateArguments(QVariantMap arguments) { From 74452bc89009753af163f6fefb2322cdb6baf45f Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 25 Feb 2016 15:34:50 -0800 Subject: [PATCH 02/21] put back some code that shouldn't have been removed --- interface/src/avatar/AvatarActionHold.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 03842ca5f9..b62cae1d58 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -157,6 +157,9 @@ std::shared_ptr AvatarActionHold::getTarget(float deltaTimeStep, glm::qu rotation = palmRotation * _relativeRotation; position = palmPosition + rotation * _relativePosition; + + // update linearVelocity based on offset via _relativePosition; + linearVelocity = linearVelocity + glm::cross(angularVelocity, position - palmPosition); }); return holdingAvatar; From 6c9cf386a698422ac4f03c777981a0e702ffebac Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 26 Feb 2016 10:14:09 -0800 Subject: [PATCH 03/21] when trigger is squeezed, check for things stuck to hand and unhook them --- examples/controllers/handControllerGrab.js | 13 +++++++++ .../entities/src/EntityScriptingInterface.cpp | 27 +++++++++++++++++++ .../entities/src/EntityScriptingInterface.h | 2 +- 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 04e93334cb..9eac023ac4 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -801,6 +801,8 @@ function MyController(hand) { this.isInitialGrab = false; this.doubleParentGrab = false; + this.checkForStrayChildren(); + if (this.state == STATE_SEARCHING ? this.triggerSmoothedReleased() : this.bumperReleased()) { this.setState(STATE_RELEASE); return; @@ -1751,6 +1753,17 @@ function MyController(hand) { return data; }; + this.checkForStrayChildren = function() { + // sometimes things can get parented to a hand and this script is unaware. Search for such entities and + // unhook them. + var handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"); + var children = Entities.getChildrenIDsOfJoint(MyAvatar.sessionUUID, handJointIndex); + children.forEach(function(childID) { + print("disconnecting stray child of hand: " + childID); + Entities.editEntity(childID, {parentID: NULL_UUID}); + }); + } + this.deactivateEntity = function(entityID, noVelocity) { var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {}); if (data && data["refCount"]) { diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index d7a47136b5..4857fb8529 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -12,6 +12,7 @@ #include "EntityItemID.h" #include +#include #include "EntitiesLogging.h" #include "EntityActionFactoryInterface.h" @@ -1063,6 +1064,32 @@ QStringList EntityScriptingInterface::getJointNames(const QUuid& entityID) { return result; } +QVector EntityScriptingInterface::getChildrenIDsOfJoint(const QUuid& parentID, int jointIndex) { + QVector result; + if (!_entityTree) { + return result; + } + _entityTree->withReadLock([&] { + QSharedPointer parentFinder = DependencyManager::get(); + if (!parentFinder) { + return; + } + bool success; + SpatiallyNestableWeakPointer parentWP = parentFinder->find(parentID, success); + if (!success) { + return; + } + SpatiallyNestablePointer parent = parentWP.lock(); + if (!parent) { + return; + } + parent->forEachChild([&](SpatiallyNestablePointer child) { + result.push_back(child->getID()); + }); + }); + return result; +} + float EntityScriptingInterface::calculateCost(float mass, float oldVelocity, float newVelocity) { return std::abs(mass * (newVelocity - oldVelocity)); } diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index fef000cc3d..79d8f0a0b0 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -168,7 +168,7 @@ public slots: Q_INVOKABLE int getJointIndex(const QUuid& entityID, const QString& name); Q_INVOKABLE QStringList getJointNames(const QUuid& entityID); - + Q_INVOKABLE QVector getChildrenIDsOfJoint(const QUuid& parentID, int jointIndex); signals: void collisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision); From 5594f1e4e586fbc3967c1ce13526915cfcc457cd Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 26 Feb 2016 10:27:34 -0800 Subject: [PATCH 04/21] if equipped entity vanishes, reset grab state --- examples/controllers/handControllerGrab.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 9eac023ac4..45d27cbbb9 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -1446,6 +1446,13 @@ function MyController(hand) { this.heartBeat(this.grabbedEntity); var props = Entities.getEntityProperties(this.grabbedEntity, ["localPosition", "parentID", "position"]); + if (!props.position) { + // server may have reset, taking our equipped entity with it. move back to "off" stte + this.setState(STATE_RELEASE); + this.callEntityMethodOnGrabbed("releaseGrab"); + return; + } + if (props.parentID == MyAvatar.sessionUUID && Vec3.length(props.localPosition) > NEAR_PICK_MAX_DISTANCE * 2.0) { // for whatever reason, the held/equipped entity has been pulled away. ungrab or unequip. From 83713898cc54ba47eaac53c8cc4f13c4ed166d83 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 26 Feb 2016 10:34:27 -0800 Subject: [PATCH 05/21] maze should delete stray balls --- unpublishedScripts/DomainContent/Home/tiltMaze/maze.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/unpublishedScripts/DomainContent/Home/tiltMaze/maze.js b/unpublishedScripts/DomainContent/Home/tiltMaze/maze.js index 557861ba30..f155c28b41 100644 --- a/unpublishedScripts/DomainContent/Home/tiltMaze/maze.js +++ b/unpublishedScripts/DomainContent/Home/tiltMaze/maze.js @@ -96,6 +96,9 @@ if (this.ballLocked === true) { return; } + if(this.ball!==null){ + Entities.deleteEntity(this.ball); + } var properties = { name: 'Hifi Tilt Maze Ball', From c0c5084149f9724a6dbab7e587e28d99e03bee4a Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 26 Feb 2016 14:05:02 -0800 Subject: [PATCH 06/21] handle parenting loops --- libraries/shared/src/SpatiallyNestable.cpp | 31 ++++++++++++++-------- libraries/shared/src/SpatiallyNestable.h | 6 ++--- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index 557b25d9b2..1a897021f1 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -15,6 +15,7 @@ #include "SpatiallyNestable.h" const float defaultAACubeSize = 1.0f; +const int maxParentingChain = 30; SpatiallyNestable::SpatiallyNestable(NestableType nestableType, QUuid id) : _nestableType(nestableType), @@ -56,14 +57,14 @@ void SpatiallyNestable::setParentID(const QUuid& parentID) { }); } -Transform SpatiallyNestable::getParentTransform(bool& success) const { +Transform SpatiallyNestable::getParentTransform(bool& success, int depth) const { Transform result; SpatiallyNestablePointer parent = getParentPointer(success); if (!success) { return result; } if (parent) { - Transform parentTransform = parent->getTransform(_parentJointIndex, success); + Transform parentTransform = parent->getTransform(_parentJointIndex, success, depth + 1); result = parentTransform.setScale(1.0f); // TODO: scaling } return result; @@ -393,11 +394,11 @@ void SpatiallyNestable::setOrientation(const glm::quat& orientation) { glm::vec3 SpatiallyNestable::getVelocity(bool& success) const { glm::vec3 result; - glm::vec3 parentVelocity = getParentVelocity(success); + Transform parentTransform = getParentTransform(success); if (!success) { return result; } - Transform parentTransform = getParentTransform(success); + glm::vec3 parentVelocity = getParentVelocity(success); if (!success) { return result; } @@ -448,11 +449,11 @@ glm::vec3 SpatiallyNestable::getParentVelocity(bool& success) const { glm::vec3 SpatiallyNestable::getAngularVelocity(bool& success) const { glm::vec3 result; - glm::vec3 parentAngularVelocity = getParentAngularVelocity(success); + Transform parentTransform = getParentTransform(success); if (!success) { return result; } - Transform parentTransform = getParentTransform(success); + glm::vec3 parentAngularVelocity = getParentAngularVelocity(success); if (!success) { return result; } @@ -499,22 +500,30 @@ glm::vec3 SpatiallyNestable::getParentAngularVelocity(bool& success) const { return result; } -const Transform SpatiallyNestable::getTransform(bool& success) const { - // return a world-space transform for this object's location - Transform parentTransform = getParentTransform(success); +const Transform SpatiallyNestable::getTransform(bool& success, int depth) const { Transform result; + // return a world-space transform for this object's location + Transform parentTransform = getParentTransform(success, depth); _transformLock.withReadLock([&] { Transform::mult(result, parentTransform, _transform); }); return result; } -const Transform SpatiallyNestable::getTransform(int jointIndex, bool& success) const { +const Transform SpatiallyNestable::getTransform(int jointIndex, bool& success, int depth) const { // this returns the world-space transform for this object. It finds its parent's transform (which may // cause this object's parent to query its parent, etc) and multiplies this object's local transform onto it. Transform jointInWorldFrame; - Transform worldTransform = getTransform(success); + if (depth > maxParentingChain) { + success = false; + // someone created a loop. break it... + qDebug() << "Parenting loop detected."; + getThisPointer()->setParentID(QUuid()); + return jointInWorldFrame; + } + + Transform worldTransform = getTransform(success, depth); worldTransform.setScale(1.0f); // TODO -- scale; if (!success) { return jointInWorldFrame; diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h index 2a8976b38f..ee6cccc7a8 100644 --- a/libraries/shared/src/SpatiallyNestable.h +++ b/libraries/shared/src/SpatiallyNestable.h @@ -52,10 +52,10 @@ public: static glm::quat localToWorld(const glm::quat& orientation, const QUuid& parentID, int parentJointIndex, bool& success); // world frame - virtual const Transform getTransform(bool& success) const; + virtual const Transform getTransform(bool& success, int depth = 0) const; virtual void setTransform(const Transform& transform, bool& success); - virtual Transform getParentTransform(bool& success) const; + virtual Transform getParentTransform(bool& success, int depth = 0) const; virtual glm::vec3 getPosition(bool& success) const; virtual glm::vec3 getPosition() const; @@ -92,7 +92,7 @@ public: virtual void setScale(const glm::vec3& scale); // get world-frame values for a specific joint - virtual const Transform getTransform(int jointIndex, bool& success) const; + virtual const Transform getTransform(int jointIndex, bool& success, int depth = 0) const; virtual glm::vec3 getPosition(int jointIndex, bool& success) const; virtual glm::vec3 getScale(int jointIndex) const; From 01ef5aabcb0b2f685db9e3a151158437bdc24c86 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 26 Feb 2016 14:38:35 -0800 Subject: [PATCH 07/21] OpenVR: More reliable detection of left and right hands --- plugins/openvr/src/ViveControllerManager.cpp | 110 +++++++++---------- plugins/openvr/src/ViveControllerManager.h | 15 +-- 2 files changed, 61 insertions(+), 64 deletions(-) diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index 071d5fd631..749c9ea2e4 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -60,10 +60,10 @@ void ViveControllerManager::activate() { [this] (bool clicked) { this->setRenderControllers(clicked); }, true, true); - if (!_hmd) { - _hmd = acquireOpenVrSystem(); + if (!_system) { + _system = acquireOpenVrSystem(); } - Q_ASSERT(_hmd); + Q_ASSERT(_system); // OpenVR provides 3d mesh representations of the controllers // Disabled controller rendering code @@ -71,7 +71,7 @@ void ViveControllerManager::activate() { auto renderModels = vr::VRRenderModels(); vr::RenderModel_t model; - if (!_hmd->LoadRenderModel(CONTROLLER_MODEL_STRING, &model)) { + if (!_system->LoadRenderModel(CONTROLLER_MODEL_STRING, &model)) { qDebug() << QString("Unable to load render model %1\n").arg(CONTROLLER_MODEL_STRING); } else { model::Mesh* mesh = new model::Mesh(); @@ -118,7 +118,7 @@ void ViveControllerManager::activate() { } */ - // unregister with UserInputMapper + // register with UserInputMapper auto userInputMapper = DependencyManager::get(); userInputMapper->registerDevice(_inputDevice); _registeredWithInputMapper = true; @@ -130,9 +130,9 @@ void ViveControllerManager::deactivate() { _container->removeMenuItem(MENU_NAME, RENDER_CONTROLLERS); _container->removeMenu(MENU_PATH); - if (_hmd) { + if (_system) { releaseOpenVrSystem(); - _hmd = nullptr; + _system = nullptr; } _inputDevice->_poseStateMap.clear(); @@ -226,56 +226,56 @@ void ViveControllerManager::pluginUpdate(float deltaTime, const controller::Inpu void ViveControllerManager::InputDevice::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) { _poseStateMap.clear(); - _buttonPressedMap.clear(); PerformanceTimer perfTimer("ViveControllerManager::update"); + auto leftHandDeviceIndex = _system->GetTrackedDeviceIndexForControllerRole(vr::TrackedControllerRole_LeftHand); + auto rightHandDeviceIndex = _system->GetTrackedDeviceIndexForControllerRole(vr::TrackedControllerRole_RightHand); + + if (!jointsCaptured) { + handleHandController(leftHandDeviceIndex, inputCalibrationData, true); + handleHandController(rightHandDeviceIndex, inputCalibrationData, false); + } + int numTrackedControllers = 0; - - for (vr::TrackedDeviceIndex_t device = vr::k_unTrackedDeviceIndex_Hmd + 1; - device < vr::k_unMaxTrackedDeviceCount && numTrackedControllers < 2; ++device) { - - if (!_hmd->IsTrackedDeviceConnected(device)) { - continue; - } - - if (_hmd->GetTrackedDeviceClass(device) != vr::TrackedDeviceClass_Controller) { - continue; - } - - if (!_trackedDevicePose[device].bPoseIsValid) { - continue; - } - + if (leftHandDeviceIndex != vr::k_unTrackedDeviceIndexInvalid) { numTrackedControllers++; - bool left = numTrackedControllers == 2; + } + if (rightHandDeviceIndex != vr::k_unTrackedDeviceIndexInvalid) { + numTrackedControllers++; + } + _trackedControllers = numTrackedControllers; +} - if (!jointsCaptured) { - const mat4& mat = _trackedDevicePoseMat4[device]; - const vec3 linearVelocity = _trackedDeviceLinearVelocities[device]; - const vec3 angularVelocity = _trackedDeviceAngularVelocities[device]; - handlePoseEvent(inputCalibrationData, mat, linearVelocity, angularVelocity, numTrackedControllers - 1); - } +void ViveControllerManager::InputDevice::handleHandController(uint32_t deviceIndex, const controller::InputCalibrationData& inputCalibrationData, bool isLeftHand) { + + if (_system->IsTrackedDeviceConnected(deviceIndex) && + _system->GetTrackedDeviceClass(deviceIndex) == vr::TrackedDeviceClass_Controller && + _trackedDevicePose[deviceIndex].bPoseIsValid) { + + // process pose + const mat4& mat = _trackedDevicePoseMat4[deviceIndex]; + const vec3 linearVelocity = _trackedDeviceLinearVelocities[deviceIndex]; + const vec3 angularVelocity = _trackedDeviceAngularVelocities[deviceIndex]; + handlePoseEvent(inputCalibrationData, mat, linearVelocity, angularVelocity, isLeftHand); - // handle inputs vr::VRControllerState_t controllerState = vr::VRControllerState_t(); - if (_hmd->GetControllerState(device, &controllerState)) { - //qDebug() << (numTrackedControllers == 1 ? "Left: " : "Right: "); - //qDebug() << "Trackpad: " << controllerState.rAxis[0].x << " " << controllerState.rAxis[0].y; - //qDebug() << "Trigger: " << controllerState.rAxis[1].x << " " << controllerState.rAxis[1].y; + if (_system->GetControllerState(deviceIndex, &controllerState)) { + + // process each button for (uint32_t i = 0; i < vr::k_EButton_Max; ++i) { auto mask = vr::ButtonMaskFromId((vr::EVRButtonId)i); bool pressed = 0 != (controllerState.ulButtonPressed & mask); - handleButtonEvent(i, pressed, left); + handleButtonEvent(i, pressed, isLeftHand); } + + // process each axis for (uint32_t i = 0; i < vr::k_unControllerStateAxisCount; i++) { - handleAxisEvent(i, controllerState.rAxis[i].x, controllerState.rAxis[i].y, left); + handleAxisEvent(i, controllerState.rAxis[i].x, controllerState.rAxis[i].y, isLeftHand); } } } - - _trackedControllers = numTrackedControllers; } void ViveControllerManager::InputDevice::focusOutEvent() { @@ -284,42 +284,38 @@ void ViveControllerManager::InputDevice::focusOutEvent() { }; // These functions do translation from the Steam IDs to the standard controller IDs -void ViveControllerManager::InputDevice::handleAxisEvent(uint32_t axis, float x, float y, bool left) { +void ViveControllerManager::InputDevice::handleAxisEvent(uint32_t axis, float x, float y, bool isLeftHand) { //FIX ME? It enters here every frame: probably we want to enter only if an event occurs axis += vr::k_EButton_Axis0; using namespace controller; if (axis == vr::k_EButton_SteamVR_Touchpad) { - _axisStateMap[left ? LX : RX] = x; - _axisStateMap[left ? LY : RY] = y; + _axisStateMap[isLeftHand ? LX : RX] = x; + _axisStateMap[isLeftHand ? LY : RY] = y; } else if (axis == vr::k_EButton_SteamVR_Trigger) { - _axisStateMap[left ? LT : RT] = x; + _axisStateMap[isLeftHand ? LT : RT] = x; } } // These functions do translation from the Steam IDs to the standard controller IDs -void ViveControllerManager::InputDevice::handleButtonEvent(uint32_t button, bool pressed, bool left) { +void ViveControllerManager::InputDevice::handleButtonEvent(uint32_t button, bool pressed, bool isLeftHand) { if (!pressed) { return; } if (button == vr::k_EButton_ApplicationMenu) { - _buttonPressedMap.insert(left ? controller::LEFT_PRIMARY_THUMB : controller::RIGHT_PRIMARY_THUMB); + _buttonPressedMap.insert(isLeftHand ? controller::LEFT_PRIMARY_THUMB : controller::RIGHT_PRIMARY_THUMB); } else if (button == vr::k_EButton_Grip) { - // Tony says these are harder to reach, so make them the meta buttons - _buttonPressedMap.insert(left ? controller::LB : controller::RB); + _buttonPressedMap.insert(isLeftHand ? controller::LB : controller::RB); } else if (button == vr::k_EButton_SteamVR_Trigger) { - _buttonPressedMap.insert(left ? controller::LT : controller::RT); + _buttonPressedMap.insert(isLeftHand ? controller::LT : controller::RT); } else if (button == vr::k_EButton_SteamVR_Touchpad) { - _buttonPressedMap.insert(left ? controller::LS : controller::RS); - } else if (button == vr::k_EButton_System) { - //FIX ME: not able to ovrewrite the behaviour of this button - _buttonPressedMap.insert(left ? controller::LEFT_SECONDARY_THUMB : controller::RIGHT_SECONDARY_THUMB); + _buttonPressedMap.insert(isLeftHand ? controller::LS : controller::RS); } } void ViveControllerManager::InputDevice::handlePoseEvent(const controller::InputCalibrationData& inputCalibrationData, const mat4& mat, const vec3& linearVelocity, - const vec3& angularVelocity, bool left) { + const vec3& angularVelocity, bool isLeftHand) { // When the sensor-to-world rotation is identity the coordinate axes look like this: // // user @@ -384,8 +380,8 @@ void ViveControllerManager::InputDevice::handlePoseEvent(const controller::Input static const glm::vec3 leftTranslationOffset = glm::vec3(-1.0f, 1.0f, 1.0f) * CONTROLLER_OFFSET; static const glm::vec3 rightTranslationOffset = CONTROLLER_OFFSET; - auto translationOffset = (left ? leftTranslationOffset : rightTranslationOffset); - auto rotationOffset = (left ? leftRotationOffset : rightRotationOffset); + auto translationOffset = (isLeftHand ? leftTranslationOffset : rightTranslationOffset); + auto rotationOffset = (isLeftHand ? leftRotationOffset : rightRotationOffset); glm::vec3 position = extractTranslation(mat); glm::quat rotation = glm::normalize(glm::quat_cast(mat)); @@ -399,7 +395,7 @@ void ViveControllerManager::InputDevice::handlePoseEvent(const controller::Input // handle change in velocity due to translationOffset avatarPose.velocity = linearVelocity + glm::cross(angularVelocity, position - extractTranslation(mat)); avatarPose.angularVelocity = angularVelocity; - _poseStateMap[left ? controller::LEFT_HAND : controller::RIGHT_HAND] = avatarPose.transform(controllerToAvatar); + _poseStateMap[isLeftHand ? controller::LEFT_HAND : controller::RIGHT_HAND] = avatarPose.transform(controllerToAvatar); } controller::Input::NamedVector ViveControllerManager::InputDevice::getAvailableInputs() const { diff --git a/plugins/openvr/src/ViveControllerManager.h b/plugins/openvr/src/ViveControllerManager.h index 51339cd465..2240a528a9 100644 --- a/plugins/openvr/src/ViveControllerManager.h +++ b/plugins/openvr/src/ViveControllerManager.h @@ -50,7 +50,7 @@ public: private: class InputDevice : public controller::InputDevice { public: - InputDevice(vr::IVRSystem*& hmd) : controller::InputDevice("Vive"), _hmd(hmd) {} + InputDevice(vr::IVRSystem*& system) : controller::InputDevice("Vive"), _system(system) {} private: // Device functions virtual controller::Input::NamedVector getAvailableInputs() const override; @@ -58,13 +58,14 @@ private: virtual void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) override; virtual void focusOutEvent() override; - void handleButtonEvent(uint32_t button, bool pressed, bool left); - void handleAxisEvent(uint32_t axis, float x, float y, bool left); + void handleHandController(uint32_t deviceIndex, const controller::InputCalibrationData& inputCalibrationData, bool isLeftHand); + void handleButtonEvent(uint32_t button, bool pressed, bool isLeftHand); + void handleAxisEvent(uint32_t axis, float x, float y, bool isLeftHand); void handlePoseEvent(const controller::InputCalibrationData& inputCalibrationData, const mat4& mat, - const vec3& linearVelocity, const vec3& angularVelocity, bool left); + const vec3& linearVelocity, const vec3& angularVelocity, bool isLeftHand); int _trackedControllers { 0 }; - vr::IVRSystem*& _hmd; + vr::IVRSystem*& _system; friend class ViveControllerManager; }; @@ -81,8 +82,8 @@ private: int _rightHandRenderID { 0 }; bool _renderControllers { false }; - vr::IVRSystem* _hmd { nullptr }; - std::shared_ptr _inputDevice { std::make_shared(_hmd) }; + vr::IVRSystem* _system { nullptr }; + std::shared_ptr _inputDevice { std::make_shared(_system) }; static const QString NAME; From 7fd88793dc3b41578680797152426b8a94c2a55b Mon Sep 17 00:00:00 2001 From: PhilipRosedale Date: Fri, 26 Feb 2016 16:41:37 -0800 Subject: [PATCH 08/21] drag relative to plane where pointer collided selection box --- examples/edit.js | 5 ++- examples/libraries/entitySelectionTool.js | 39 +++++++++++++++++++---- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/examples/edit.js b/examples/edit.js index 50a66ea31f..fc04a0b26f 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -758,7 +758,8 @@ function findClickedEntity(event) { var foundEntity = result.entityID; return { pickRay: pickRay, - entityID: foundEntity + entityID: foundEntity, + intersection: result.intersection }; } @@ -978,6 +979,8 @@ function mouseClickEvent(event) { if (!event.isShifted) { selectionManager.setSelections([foundEntity]); + Vec3.print("found object, intersection = ", result.intersection); + selectionManager.setPickPlanePosition(result.intersection); } else { selectionManager.addEntity(foundEntity, true); } diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index c1675ef044..6a7247aa57 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -16,6 +16,11 @@ HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; SPACE_LOCAL = "local"; SPACE_WORLD = "world"; +function objectTranslationPlanePoint(position, dimensions) { + var newPosition = { x: position.x, y: position.y, z: position.z }; + newPosition.y -= dimensions.y / 2.0; + return newPosition; +} SelectionManager = (function() { var that = {}; @@ -82,6 +87,11 @@ SelectionManager = (function() { y: 0, z: 0 }; + that.pickPlanePosition = { + x: 0, + y: 0, + z: 0 + }; that.saveProperties = function() { that.savedProperties = {}; @@ -109,6 +119,13 @@ SelectionManager = (function() { that._update(); }; + that.setPickPlanePosition = function(position) { + that.pickPlanePosition.x = position.x; + that.pickPlanePosition.y = position.y; + that.pickPlanePosition.z = position.z; + Vec3.print("that.pickPlanePosition = ", that.pickPlanePosition); + }; + that.addEntity = function(entityID, toggleSelection) { if (entityID) { var idx = -1; @@ -2252,15 +2269,17 @@ SelectionDisplay = (function() { var constrainMajorOnly = false; var startPosition = null; var duplicatedEntityIDs = null; + var translateXZTool = { mode: 'TRANSLATE_XZ', + pickPlanePosition: { x: 0, y: 0, z: 0 }, onBegin: function(event) { SelectionManager.saveProperties(); startPosition = SelectionManager.worldPosition; var dimensions = SelectionManager.worldDimensions; var pickRay = Camera.computePickRay(event.x, event.y); - initialXZPick = rayPlaneIntersection(pickRay, startPosition, { + initialXZPick = rayPlaneIntersection(pickRay, translateXZTool.pickPlanePosition, { x: 0, y: 1, z: 0 @@ -2300,13 +2319,23 @@ SelectionDisplay = (function() { onMove: function(event) { pickRay = Camera.computePickRay(event.x, event.y); - var pick = rayPlaneIntersection(pickRay, SelectionManager.worldPosition, { + var pick = rayPlaneIntersection(pickRay, translateXZTool.pickPlanePosition, { x: 0, y: 1, z: 0 }); + var vector = Vec3.subtract(pick, initialXZPick); + Vec3.print("Pick Plane Position", translateXZTool.pickPlanePosition); + + /* + if (pickRay.origin.y < this.pickPlanePosition.y) { + vector.x *= -1.0; + vector.z *= -1.0; + } */ + + // If shifted, constrain to one axis if (event.isShifted) { if (Math.abs(vector.x) > Math.abs(vector.z)) { @@ -2645,11 +2674,6 @@ SelectionDisplay = (function() { pickRayPosition, planeNormal); - // Overlays.editOverlay(normalLine, { - // start: initialPosition, - // end: Vec3.sum(Vec3.multiply(100000, planeNormal), initialPosition), - // }); - SelectionManager.saveProperties(); }; @@ -4093,6 +4117,7 @@ SelectionDisplay = (function() { switch (result.overlayID) { case selectionBox: activeTool = translateXZTool; + translateXZTool.pickPlanePosition = result.intersection; mode = translateXZTool.mode; activeTool.onBegin(event); somethingClicked = true; From 47504a068ee43969869c3e426c2ba2427b7316b3 Mon Sep 17 00:00:00 2001 From: PhilipRosedale Date: Fri, 26 Feb 2016 16:50:37 -0800 Subject: [PATCH 09/21] remove unused code --- examples/edit.js | 2 -- examples/libraries/entitySelectionTool.js | 21 --------------------- 2 files changed, 23 deletions(-) diff --git a/examples/edit.js b/examples/edit.js index fc04a0b26f..0e252bdf86 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -979,8 +979,6 @@ function mouseClickEvent(event) { if (!event.isShifted) { selectionManager.setSelections([foundEntity]); - Vec3.print("found object, intersection = ", result.intersection); - selectionManager.setPickPlanePosition(result.intersection); } else { selectionManager.addEntity(foundEntity, true); } diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index 6a7247aa57..015e169908 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -87,11 +87,6 @@ SelectionManager = (function() { y: 0, z: 0 }; - that.pickPlanePosition = { - x: 0, - y: 0, - z: 0 - }; that.saveProperties = function() { that.savedProperties = {}; @@ -119,13 +114,6 @@ SelectionManager = (function() { that._update(); }; - that.setPickPlanePosition = function(position) { - that.pickPlanePosition.x = position.x; - that.pickPlanePosition.y = position.y; - that.pickPlanePosition.z = position.z; - Vec3.print("that.pickPlanePosition = ", that.pickPlanePosition); - }; - that.addEntity = function(entityID, toggleSelection) { if (entityID) { var idx = -1; @@ -2327,15 +2315,6 @@ SelectionDisplay = (function() { var vector = Vec3.subtract(pick, initialXZPick); - Vec3.print("Pick Plane Position", translateXZTool.pickPlanePosition); - - /* - if (pickRay.origin.y < this.pickPlanePosition.y) { - vector.x *= -1.0; - vector.z *= -1.0; - } */ - - // If shifted, constrain to one axis if (event.isShifted) { if (Math.abs(vector.x) > Math.abs(vector.z)) { From 89885805d22ddf3a2c952533a8d97206d95de26e Mon Sep 17 00:00:00 2001 From: Anthony Thibault Date: Fri, 26 Feb 2016 18:34:33 -0800 Subject: [PATCH 10/21] OpenVR: Added hysteresis to the touch pad dead spot This should make using the Vive touch pad for movement more reliable. --- plugins/openvr/src/ViveControllerManager.cpp | 38 ++++++++++++-------- plugins/openvr/src/ViveControllerManager.h | 37 +++++++++++++++---- 2 files changed, 54 insertions(+), 21 deletions(-) diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index 749c9ea2e4..0de8f6891d 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -234,8 +234,8 @@ void ViveControllerManager::InputDevice::update(float deltaTime, const controlle auto rightHandDeviceIndex = _system->GetTrackedDeviceIndexForControllerRole(vr::TrackedControllerRole_RightHand); if (!jointsCaptured) { - handleHandController(leftHandDeviceIndex, inputCalibrationData, true); - handleHandController(rightHandDeviceIndex, inputCalibrationData, false); + handleHandController(deltaTime, leftHandDeviceIndex, inputCalibrationData, true); + handleHandController(deltaTime, rightHandDeviceIndex, inputCalibrationData, false); } int numTrackedControllers = 0; @@ -248,7 +248,7 @@ void ViveControllerManager::InputDevice::update(float deltaTime, const controlle _trackedControllers = numTrackedControllers; } -void ViveControllerManager::InputDevice::handleHandController(uint32_t deviceIndex, const controller::InputCalibrationData& inputCalibrationData, bool isLeftHand) { +void ViveControllerManager::InputDevice::handleHandController(float deltaTime, uint32_t deviceIndex, const controller::InputCalibrationData& inputCalibrationData, bool isLeftHand) { if (_system->IsTrackedDeviceConnected(deviceIndex) && _system->GetTrackedDeviceClass(deviceIndex) == vr::TrackedDeviceClass_Controller && @@ -258,7 +258,7 @@ void ViveControllerManager::InputDevice::handleHandController(uint32_t deviceInd const mat4& mat = _trackedDevicePoseMat4[deviceIndex]; const vec3 linearVelocity = _trackedDeviceLinearVelocities[deviceIndex]; const vec3 angularVelocity = _trackedDeviceAngularVelocities[deviceIndex]; - handlePoseEvent(inputCalibrationData, mat, linearVelocity, angularVelocity, isLeftHand); + handlePoseEvent(deltaTime, inputCalibrationData, mat, linearVelocity, angularVelocity, isLeftHand); vr::VRControllerState_t controllerState = vr::VRControllerState_t(); if (_system->GetControllerState(deviceIndex, &controllerState)) { @@ -267,12 +267,12 @@ void ViveControllerManager::InputDevice::handleHandController(uint32_t deviceInd for (uint32_t i = 0; i < vr::k_EButton_Max; ++i) { auto mask = vr::ButtonMaskFromId((vr::EVRButtonId)i); bool pressed = 0 != (controllerState.ulButtonPressed & mask); - handleButtonEvent(i, pressed, isLeftHand); + handleButtonEvent(deltaTime, i, pressed, isLeftHand); } // process each axis for (uint32_t i = 0; i < vr::k_unControllerStateAxisCount; i++) { - handleAxisEvent(i, controllerState.rAxis[i].x, controllerState.rAxis[i].y, isLeftHand); + handleAxisEvent(deltaTime, i, controllerState.rAxis[i].x, controllerState.rAxis[i].y, isLeftHand); } } } @@ -284,36 +284,44 @@ void ViveControllerManager::InputDevice::focusOutEvent() { }; // These functions do translation from the Steam IDs to the standard controller IDs -void ViveControllerManager::InputDevice::handleAxisEvent(uint32_t axis, float x, float y, bool isLeftHand) { +void ViveControllerManager::InputDevice::handleAxisEvent(float deltaTime, uint32_t axis, float x, float y, bool isLeftHand) { //FIX ME? It enters here every frame: probably we want to enter only if an event occurs axis += vr::k_EButton_Axis0; using namespace controller; + if (axis == vr::k_EButton_SteamVR_Touchpad) { - _axisStateMap[isLeftHand ? LX : RX] = x; - _axisStateMap[isLeftHand ? LY : RY] = y; + glm::vec2 stick(x, y); + if (isLeftHand) { + stick = _filteredLeftStick.process(deltaTime, stick); + } else { + stick = _filteredRightStick.process(deltaTime, stick); + } + _axisStateMap[isLeftHand ? LX : RX] = stick.x; + _axisStateMap[isLeftHand ? LY : RY] = stick.y; } else if (axis == vr::k_EButton_SteamVR_Trigger) { _axisStateMap[isLeftHand ? LT : RT] = x; } } // These functions do translation from the Steam IDs to the standard controller IDs -void ViveControllerManager::InputDevice::handleButtonEvent(uint32_t button, bool pressed, bool isLeftHand) { +void ViveControllerManager::InputDevice::handleButtonEvent(float deltaTime, uint32_t button, bool pressed, bool isLeftHand) { if (!pressed) { return; } + using namespace controller; if (button == vr::k_EButton_ApplicationMenu) { - _buttonPressedMap.insert(isLeftHand ? controller::LEFT_PRIMARY_THUMB : controller::RIGHT_PRIMARY_THUMB); + _buttonPressedMap.insert(isLeftHand ? LEFT_PRIMARY_THUMB : RIGHT_PRIMARY_THUMB); } else if (button == vr::k_EButton_Grip) { - _buttonPressedMap.insert(isLeftHand ? controller::LB : controller::RB); + _buttonPressedMap.insert(isLeftHand ? LB : RB); } else if (button == vr::k_EButton_SteamVR_Trigger) { - _buttonPressedMap.insert(isLeftHand ? controller::LT : controller::RT); + _buttonPressedMap.insert(isLeftHand ? LT : RT); } else if (button == vr::k_EButton_SteamVR_Touchpad) { - _buttonPressedMap.insert(isLeftHand ? controller::LS : controller::RS); + _buttonPressedMap.insert(isLeftHand ? LS : RS); } } -void ViveControllerManager::InputDevice::handlePoseEvent(const controller::InputCalibrationData& inputCalibrationData, +void ViveControllerManager::InputDevice::handlePoseEvent(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, const mat4& mat, const vec3& linearVelocity, const vec3& angularVelocity, bool isLeftHand) { // When the sensor-to-world rotation is identity the coordinate axes look like this: diff --git a/plugins/openvr/src/ViveControllerManager.h b/plugins/openvr/src/ViveControllerManager.h index 2240a528a9..480fbfeb90 100644 --- a/plugins/openvr/src/ViveControllerManager.h +++ b/plugins/openvr/src/ViveControllerManager.h @@ -58,12 +58,39 @@ private: virtual void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) override; virtual void focusOutEvent() override; - void handleHandController(uint32_t deviceIndex, const controller::InputCalibrationData& inputCalibrationData, bool isLeftHand); - void handleButtonEvent(uint32_t button, bool pressed, bool isLeftHand); - void handleAxisEvent(uint32_t axis, float x, float y, bool isLeftHand); - void handlePoseEvent(const controller::InputCalibrationData& inputCalibrationData, const mat4& mat, + void handleHandController(float deltaTime, uint32_t deviceIndex, const controller::InputCalibrationData& inputCalibrationData, bool isLeftHand); + void handleButtonEvent(float deltaTime, uint32_t button, bool pressed, bool isLeftHand); + void handleAxisEvent(float deltaTime, uint32_t axis, float x, float y, bool isLeftHand); + void handlePoseEvent(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, const mat4& mat, const vec3& linearVelocity, const vec3& angularVelocity, bool isLeftHand); + class FilteredStick { + public: + glm::vec2 process(float deltaTime, const glm::vec2& stick) { + // Use a timer to prevent the stick going to back to zero. + // This to work around the noisy touch pad that will flash back to zero breifly + const float ZERO_HYSTERESIS_PERIOD = 0.2f; // 200 ms + if (glm::length(stick) == 0.0f) { + if (_timer <= 0.0f) { + return glm::vec2(0.0f, 0.0f); + } else { + _timer -= deltaTime; + return _stick; + } + } else { + _timer = ZERO_HYSTERESIS_PERIOD; + _stick = stick; + return stick; + } + } + protected: + float _timer { 0.0f }; + glm::vec2 _stick { 0.0f, 0.0f }; + }; + + FilteredStick _filteredLeftStick; + FilteredStick _filteredRightStick; + int _trackedControllers { 0 }; vr::IVRSystem*& _system; friend class ViveControllerManager; @@ -71,8 +98,6 @@ private: void renderHand(const controller::Pose& pose, gpu::Batch& batch, int sign); - - bool _registeredWithInputMapper { false }; bool _modelLoaded { false }; model::Geometry _modelGeometry; From 1d681a24618fa4bfe35f55de4eae40006a0970c7 Mon Sep 17 00:00:00 2001 From: PhilipRosedale Date: Sun, 28 Feb 2016 12:00:13 -0800 Subject: [PATCH 11/21] Add angular size and azimuth limits to object XZ translation --- examples/edit.js | 22 ++++++++- examples/libraries/entitySelectionTool.js | 56 +++++++++++++++++++++-- 2 files changed, 73 insertions(+), 5 deletions(-) diff --git a/examples/edit.js b/examples/edit.js index 0e252bdf86..d77e017c47 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -711,9 +711,27 @@ var intersection; var SCALE_FACTOR = 200.0; -function rayPlaneIntersection(pickRay, point, normal) { +function rayPlaneIntersection(pickRay, point, normal) { // + // + // This version of the test returns the intersection of a line with a plane + // + var collides = Vec3.dot(pickRay.direction, normal); + var d = -Vec3.dot(point, normal); - var t = -(Vec3.dot(pickRay.origin, normal) + d) / Vec3.dot(pickRay.direction, normal); + var t = -(Vec3.dot(pickRay.origin, normal) + d) / collides; + + return Vec3.sum(pickRay.origin, Vec3.multiply(pickRay.direction, t)); +} + +function rayPlaneIntersection2(pickRay, point, normal) { + // + // This version of the test returns false if the ray is directed away from the plane + // + var collides = Vec3.dot(pickRay.direction, normal); + if (collides > 0.0) return false; + + var d = -Vec3.dot(point, normal); + var t = -(Vec3.dot(pickRay.origin, normal) + d) / collides; return Vec3.sum(pickRay.origin, Vec3.multiply(pickRay.direction, t)); } diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index 015e169908..0308a092ed 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -2261,6 +2261,10 @@ SelectionDisplay = (function() { var translateXZTool = { mode: 'TRANSLATE_XZ', pickPlanePosition: { x: 0, y: 0, z: 0 }, + greatestDimension: 0.0, + startingDistance: 0.0, + startingAzimuth: 0.0, + lastVector: { x: 0, y: 0, z: 0 }, onBegin: function(event) { SelectionManager.saveProperties(); startPosition = SelectionManager.worldPosition; @@ -2304,17 +2308,56 @@ SelectionDisplay = (function() { visible: false }); }, + azimuth: function(origin, intersection) { + return (origin.y - intersection.y) / Vec3.distance(origin, intersection); + }, onMove: function(event) { + var wantDebug = false; pickRay = Camera.computePickRay(event.x, event.y); - var pick = rayPlaneIntersection(pickRay, translateXZTool.pickPlanePosition, { + var pick = rayPlaneIntersection2(pickRay, translateXZTool.pickPlanePosition, { x: 0, y: 1, z: 0 }); + // If the pick ray doesn't hit the pick plane in this direction, do nothing. + // this will happen when someone drags across the horizon from the side they started on. + if (!pick) { + if (wantDebug) { + print("Pick ray does not intersect XZ plane."); + } + return; + } + var vector = Vec3.subtract(pick, initialXZPick); + // If the mouse is too close to the horizon of the pick plane, stop moving + var MIN_AZIMUTH = 0.02; // Radians + var azimuth = translateXZTool.azimuth(pickRay.origin, pick); + if ((translateXZTool.startingAzimuth > 0.0 && azimuth < MIN_AZIMUTH) || + (translateXZTool.startingAzimuth < 0.0 && azimuth > MIN_AZIMUTH)) { + //vector = translateXZTool.lastVector; + if (wantDebug) { + print("Azimuth = " + azimuth); + } + return; + } + + // If the angular size of the object is too small, stop moving + var MIN_ANGULAR_SIZE = 0.01; // Radians + if (translateXZTool.greatestDimension > 0) { + var angularSize = Math.atan(translateXZTool.greatestDimension / Vec3.distance(pickRay.origin, pick)); + if (wantDebug) { + print("Angular size = " + angularSize); + } + if (angularSize < MIN_ANGULAR_SIZE) { + return; + } + } + + translateXZTool.lastVector = vector; + // If shifted, constrain to one axis if (event.isShifted) { if (Math.abs(vector.x) > Math.abs(vector.z)) { @@ -2376,7 +2419,7 @@ SelectionDisplay = (function() { grid.snapToGrid(Vec3.sum(cornerPosition, vector), constrainMajorOnly), cornerPosition); - var wantDebug = false; + for (var i = 0; i < SelectionManager.selections.length; i++) { var properties = SelectionManager.savedProperties[SelectionManager.selections[i]]; @@ -3754,7 +3797,7 @@ SelectionDisplay = (function() { }; that.mousePressEvent = function(event) { - + if (!event.isLeftButton) { // if another mouse button than left is pressed ignore it return false; @@ -4097,6 +4140,13 @@ SelectionDisplay = (function() { case selectionBox: activeTool = translateXZTool; translateXZTool.pickPlanePosition = result.intersection; + translateXZTool.greatestDimension = Math.max(Math.max(SelectionManager.worldDimensions.x, SelectionManager.worldDimensions.y), + SelectionManager.worldDimensions.z); + print("longest dimension" + translateXZTool.greatestDimension); + translateXZTool.startingDistance = Vec3.distance(pickRay.origin, SelectionManager.position); + print("starting distance" + translateXZTool.startingDistance); + translateXZTool.startingAzimuth = translateXZTool.azimuth(pickRay.origin, translateXZTool.pickPlanePosition); + print("starting azimuth" + translateXZTool.startingAzimuth); mode = translateXZTool.mode; activeTool.onBegin(event); somethingClicked = true; From 5bf8670bc2e3f6def32d4fddf237b1c1c490a040 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sun, 28 Feb 2016 12:15:26 -0800 Subject: [PATCH 12/21] fix SpatiallyNestable::getChildren --- examples/controllers/handControllerGrab.js | 3 ++- libraries/entities/src/EntityScriptingInterface.cpp | 4 +++- libraries/shared/src/SpatiallyNestable.cpp | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 45d27cbbb9..2bddc5d677 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -1,3 +1,4 @@ +"use strict"; // handControllerGrab.js // // Created by Eric Levin on 9/2/15 @@ -1766,7 +1767,7 @@ function MyController(hand) { var handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"); var children = Entities.getChildrenIDsOfJoint(MyAvatar.sessionUUID, handJointIndex); children.forEach(function(childID) { - print("disconnecting stray child of hand: " + childID); + print("disconnecting stray child of hand: (" + _this.hand + ") " + childID); Entities.editEntity(childID, {parentID: NULL_UUID}); }); } diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 4857fb8529..26f73eb65a 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -1084,7 +1084,9 @@ QVector EntityScriptingInterface::getChildrenIDsOfJoint(const QUuid& pare return; } parent->forEachChild([&](SpatiallyNestablePointer child) { - result.push_back(child->getID()); + if (child->getParentJointIndex() == jointIndex) { + result.push_back(child->getID()); + } }); }); return result; diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index 557b25d9b2..2704a8bc21 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -682,7 +682,7 @@ QList SpatiallyNestable::getChildren() const { _childrenLock.withReadLock([&] { foreach(SpatiallyNestableWeakPointer childWP, _children.values()) { SpatiallyNestablePointer child = childWP.lock(); - if (child) { + if (child && child->_parentKnowsMe && child->getParentID() == getID()) { children << child; } } From e9aed839205ec314733215c2febf1be85caa508b Mon Sep 17 00:00:00 2001 From: PhilipRosedale Date: Sun, 28 Feb 2016 12:41:40 -0800 Subject: [PATCH 13/21] suppress debug messages --- examples/edit.js | 14 +++++--- examples/libraries/entitySelectionTool.js | 42 +++++++++++++---------- 2 files changed, 34 insertions(+), 22 deletions(-) diff --git a/examples/edit.js b/examples/edit.js index d77e017c47..54311a8c42 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -945,6 +945,7 @@ function mouseReleaseEvent(event) { } function mouseClickEvent(event) { + var wantDebug = false; if (isActive && event.isLeftButton) { var result = findClickedEntity(event); if (result === null) { @@ -959,11 +960,15 @@ function mouseClickEvent(event) { var properties = Entities.getEntityProperties(foundEntity); if (isLocked(properties)) { - print("Model locked " + properties.id); + if (wantDebug) { + print("Model locked " + properties.id); + } } else { var halfDiagonal = Vec3.length(properties.dimensions) / 2.0; - print("Checking properties: " + properties.id + " " + " - Half Diagonal:" + halfDiagonal); + if (wantDebug) { + print("Checking properties: " + properties.id + " " + " - Half Diagonal:" + halfDiagonal); + } // P P - Model // /| A - Palm // / | d B - unit vector toward tip @@ -1000,8 +1005,9 @@ function mouseClickEvent(event) { } else { selectionManager.addEntity(foundEntity, true); } - - print("Model selected: " + foundEntity); + if (wantDebug) { + print("Model selected: " + foundEntity); + } selectionDisplay.select(selectedEntityID, event); if (Menu.isOptionChecked(MENU_AUTO_FOCUS_ON_SELECT)) { diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index 0308a092ed..3627a0a05e 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -2264,7 +2264,6 @@ SelectionDisplay = (function() { greatestDimension: 0.0, startingDistance: 0.0, startingAzimuth: 0.0, - lastVector: { x: 0, y: 0, z: 0 }, onBegin: function(event) { SelectionManager.saveProperties(); startPosition = SelectionManager.worldPosition; @@ -2337,7 +2336,6 @@ SelectionDisplay = (function() { var azimuth = translateXZTool.azimuth(pickRay.origin, pick); if ((translateXZTool.startingAzimuth > 0.0 && azimuth < MIN_AZIMUTH) || (translateXZTool.startingAzimuth < 0.0 && azimuth > MIN_AZIMUTH)) { - //vector = translateXZTool.lastVector; if (wantDebug) { print("Azimuth = " + azimuth); } @@ -2356,8 +2354,6 @@ SelectionDisplay = (function() { } } - translateXZTool.lastVector = vector; - // If shifted, constrain to one axis if (event.isShifted) { if (Math.abs(vector.x) > Math.abs(vector.z)) { @@ -3797,7 +3793,7 @@ SelectionDisplay = (function() { }; that.mousePressEvent = function(event) { - + var wantDebug = false; if (!event.isLeftButton) { // if another mouse button than left is pressed ignore it return false; @@ -3823,7 +3819,7 @@ SelectionDisplay = (function() { if (result.intersects) { - var wantDebug = false; + if (wantDebug) { print("something intersects... "); print(" result.overlayID:" + result.overlayID + "[" + overlayNames[result.overlayID] + "]"); @@ -3920,7 +3916,10 @@ SelectionDisplay = (function() { if (!somethingClicked) { - print("rotate handle case..."); + if (wantDebug) { + print("rotate handle case..."); + } + // After testing our stretch handles, then check out rotate handles Overlays.editOverlay(yawHandle, { @@ -3988,15 +3987,17 @@ SelectionDisplay = (function() { break; default: - print("mousePressEvent()...... " + overlayNames[result.overlayID]); + if (wantDebug) { + print("mousePressEvent()...... " + overlayNames[result.overlayID]); + } mode = "UNKNOWN"; break; } } - - print(" somethingClicked:" + somethingClicked); - print(" mode:" + mode); - + if (wantDebug) { + print(" somethingClicked:" + somethingClicked); + print(" mode:" + mode); + } if (somethingClicked) { @@ -4142,17 +4143,22 @@ SelectionDisplay = (function() { translateXZTool.pickPlanePosition = result.intersection; translateXZTool.greatestDimension = Math.max(Math.max(SelectionManager.worldDimensions.x, SelectionManager.worldDimensions.y), SelectionManager.worldDimensions.z); - print("longest dimension" + translateXZTool.greatestDimension); - translateXZTool.startingDistance = Vec3.distance(pickRay.origin, SelectionManager.position); - print("starting distance" + translateXZTool.startingDistance); - translateXZTool.startingAzimuth = translateXZTool.azimuth(pickRay.origin, translateXZTool.pickPlanePosition); - print("starting azimuth" + translateXZTool.startingAzimuth); + if (wantDebug) { + print("longest dimension: " + translateXZTool.greatestDimension); + translateXZTool.startingDistance = Vec3.distance(pickRay.origin, SelectionManager.position); + print("starting distance: " + translateXZTool.startingDistance); + translateXZTool.startingAzimuth = translateXZTool.azimuth(pickRay.origin, translateXZTool.pickPlanePosition); + print(" starting azimuth: " + translateXZTool.startingAzimuth); + } + mode = translateXZTool.mode; activeTool.onBegin(event); somethingClicked = true; break; default: - print("mousePressEvent()...... " + overlayNames[result.overlayID]); + if (wantDebug) { + print("mousePressEvent()...... " + overlayNames[result.overlayID]); + } mode = "UNKNOWN"; break; } From 8de4a5907861d689c450bfc4f5ab45d9bb57e917 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Sun, 28 Feb 2016 13:06:49 -0800 Subject: [PATCH 14/21] add support for auto-hide reticle and seek lookat on auto-show of reticle --- examples/autoHideReticle.js | 70 ++++++++++++++++++++++ interface/src/ui/ApplicationCompositor.cpp | 26 ++++---- 2 files changed, 84 insertions(+), 12 deletions(-) create mode 100644 examples/autoHideReticle.js diff --git a/examples/autoHideReticle.js b/examples/autoHideReticle.js new file mode 100644 index 0000000000..172dd9b9f7 --- /dev/null +++ b/examples/autoHideReticle.js @@ -0,0 +1,70 @@ +// autoHideReticle.js +// examples +// +// Created by Brad Hefta-Gaub on 2/23/16. +// Copyright 2016 High Fidelity, Inc. +// +// This script will auto-hide the reticle on inactivity... and then if it detects movement after being hidden +// it will automatically move the reticle to the look at position +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var MINIMUM_SEEK_DISTANCE = 0.01; +var NON_LINEAR_DIVISOR = 2; + +var lastMouseMove = Date.now(); +var HIDE_STATIC_MOUSE_AFTER = 5000; // 5 seconds +var seekToLookAt = false; + +Controller.mouseMoveEvent.connect(function(mouseEvent) { + lastMouseMove = Date.now(); + + // if the reticle is hidden, show it... + if (!Reticle.visible) { + Reticle.visible = true; + if (HMD.active) { + seekToLookAt = true; + } + } +}); + + + +Script.update.connect(function(deltaTime) { + + // if we're currently seeking the lookAt move the mouse toward the lookat + if (HMD.active && seekToLookAt) { + var lookAt2D = HMD.getHUDLookAtPosition2D(); + var currentReticlePosition = Reticle.position; + var distanceBetweenX = lookAt2D.x - Reticle.position.x; + var distanceBetweenY = lookAt2D.y - Reticle.position.y; + var moveX = distanceBetweenX / NON_LINEAR_DIVISOR; + var moveY = distanceBetweenY / NON_LINEAR_DIVISOR; + var newPosition = { x: Reticle.position.x + moveX, y: Reticle.position.y + moveY }; + var closeEnoughX = false; + var closeEnoughY = false; + if (moveX < MINIMUM_SEEK_DISTANCE) { + newPosition.x = lookAt2D.x; + closeEnoughX = true; + } + if (moveY < MINIMUM_SEEK_DISTANCE) { + newPosition.y = lookAt2D.y; + closeEnoughY = true; + } + if (closeEnoughX && closeEnoughY) { + seekToLookAt = false; + } + Reticle.position = newPosition; + } + + // if we haven't moved in a long period of time, hide the reticle + if (Reticle.visible) { + var now = Date.now(); + var timeSinceLastMouseMove = now - lastMouseMove; + if (timeSinceLastMouseMove > HIDE_STATIC_MOUSE_AFTER) { + Reticle.visible = false; + } + } +}); diff --git a/interface/src/ui/ApplicationCompositor.cpp b/interface/src/ui/ApplicationCompositor.cpp index 59d794d7cb..e8e6a0a956 100644 --- a/interface/src/ui/ApplicationCompositor.cpp +++ b/interface/src/ui/ApplicationCompositor.cpp @@ -212,19 +212,21 @@ void ApplicationCompositor::displayOverlayTexture(RenderArgs* renderArgs) { geometryCache->renderUnitQuad(batch, vec4(vec3(1), _alpha)); //draw the mouse pointer - // Get the mouse coordinates and convert to NDC [-1, 1] - vec2 canvasSize = qApp->getCanvasSize(); // desktop, use actual canvas... - vec2 mousePosition = toNormalizedDeviceScale(vec2(qApp->getMouse()), canvasSize); - // Invert the Y axis - mousePosition.y *= -1.0f; + if (getReticleVisible()) { + // Get the mouse coordinates and convert to NDC [-1, 1] + vec2 canvasSize = qApp->getCanvasSize(); // desktop, use actual canvas... + vec2 mousePosition = toNormalizedDeviceScale(vec2(qApp->getMouse()), canvasSize); + // Invert the Y axis + mousePosition.y *= -1.0f; - Transform model; - model.setTranslation(vec3(mousePosition, 0)); - vec2 mouseSize = CURSOR_PIXEL_SIZE / canvasSize; - model.setScale(vec3(mouseSize, 1.0f)); - batch.setModelTransform(model); - bindCursorTexture(batch); - geometryCache->renderUnitQuad(batch, vec4(1)); + Transform model; + model.setTranslation(vec3(mousePosition, 0)); + vec2 mouseSize = CURSOR_PIXEL_SIZE / canvasSize; + model.setScale(vec3(mouseSize, 1.0f)); + batch.setModelTransform(model); + bindCursorTexture(batch); + geometryCache->renderUnitQuad(batch, vec4(1)); + } }); } From cee79c8274865f2dcd6653798832fe58fae8ae29 Mon Sep 17 00:00:00 2001 From: PhilipRosedale Date: Sun, 28 Feb 2016 15:44:23 -0800 Subject: [PATCH 15/21] Fixed bug in rayPlane test --- examples/edit.js | 5 +++-- examples/libraries/entitySelectionTool.js | 7 +++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/examples/edit.js b/examples/edit.js index 54311a8c42..d39a20c2e3 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -728,9 +728,10 @@ function rayPlaneIntersection2(pickRay, point, normal) { // This version of the test returns false if the ray is directed away from the plane // var collides = Vec3.dot(pickRay.direction, normal); - if (collides > 0.0) return false; - var d = -Vec3.dot(point, normal); + if (((collides > 0.0) && (d > 0.0)) || ((collides < 0.0) && (d < 0.0))) { + return false; + } var t = -(Vec3.dot(pickRay.origin, normal) + d) / collides; return Vec3.sum(pickRay.origin, Vec3.multiply(pickRay.direction, t)); diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index 3627a0a05e..1834962e77 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -2334,10 +2334,13 @@ SelectionDisplay = (function() { // If the mouse is too close to the horizon of the pick plane, stop moving var MIN_AZIMUTH = 0.02; // Radians var azimuth = translateXZTool.azimuth(pickRay.origin, pick); + if (wantDebug) { + print("Start Azimuth: " + translateXZTool.startingAzimuth + ", Azimuth: " + azimuth); + } if ((translateXZTool.startingAzimuth > 0.0 && azimuth < MIN_AZIMUTH) || - (translateXZTool.startingAzimuth < 0.0 && azimuth > MIN_AZIMUTH)) { + (translateXZTool.startingAzimuth < 0.0 && azimuth > -MIN_AZIMUTH)) { if (wantDebug) { - print("Azimuth = " + azimuth); + print("too close to horizon!"); } return; } From 4009f03c384a553dcb708941958f84df30da410f Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 29 Feb 2016 08:38:53 -0800 Subject: [PATCH 16/21] move autohide/show behavior to depthReticle, make it a default script --- examples/autoHideReticle.js | 70 ------------------------------------ examples/defaultScripts.js | 1 + examples/depthReticle.js | 71 ++++++++++++++++++++++++++++++++++++- 3 files changed, 71 insertions(+), 71 deletions(-) delete mode 100644 examples/autoHideReticle.js diff --git a/examples/autoHideReticle.js b/examples/autoHideReticle.js deleted file mode 100644 index 172dd9b9f7..0000000000 --- a/examples/autoHideReticle.js +++ /dev/null @@ -1,70 +0,0 @@ -// autoHideReticle.js -// examples -// -// Created by Brad Hefta-Gaub on 2/23/16. -// Copyright 2016 High Fidelity, Inc. -// -// This script will auto-hide the reticle on inactivity... and then if it detects movement after being hidden -// it will automatically move the reticle to the look at position -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -var MINIMUM_SEEK_DISTANCE = 0.01; -var NON_LINEAR_DIVISOR = 2; - -var lastMouseMove = Date.now(); -var HIDE_STATIC_MOUSE_AFTER = 5000; // 5 seconds -var seekToLookAt = false; - -Controller.mouseMoveEvent.connect(function(mouseEvent) { - lastMouseMove = Date.now(); - - // if the reticle is hidden, show it... - if (!Reticle.visible) { - Reticle.visible = true; - if (HMD.active) { - seekToLookAt = true; - } - } -}); - - - -Script.update.connect(function(deltaTime) { - - // if we're currently seeking the lookAt move the mouse toward the lookat - if (HMD.active && seekToLookAt) { - var lookAt2D = HMD.getHUDLookAtPosition2D(); - var currentReticlePosition = Reticle.position; - var distanceBetweenX = lookAt2D.x - Reticle.position.x; - var distanceBetweenY = lookAt2D.y - Reticle.position.y; - var moveX = distanceBetweenX / NON_LINEAR_DIVISOR; - var moveY = distanceBetweenY / NON_LINEAR_DIVISOR; - var newPosition = { x: Reticle.position.x + moveX, y: Reticle.position.y + moveY }; - var closeEnoughX = false; - var closeEnoughY = false; - if (moveX < MINIMUM_SEEK_DISTANCE) { - newPosition.x = lookAt2D.x; - closeEnoughX = true; - } - if (moveY < MINIMUM_SEEK_DISTANCE) { - newPosition.y = lookAt2D.y; - closeEnoughY = true; - } - if (closeEnoughX && closeEnoughY) { - seekToLookAt = false; - } - Reticle.position = newPosition; - } - - // if we haven't moved in a long period of time, hide the reticle - if (Reticle.visible) { - var now = Date.now(); - var timeSinceLastMouseMove = now - lastMouseMove; - if (timeSinceLastMouseMove > HIDE_STATIC_MOUSE_AFTER) { - Reticle.visible = false; - } - } -}); diff --git a/examples/defaultScripts.js b/examples/defaultScripts.js index 35af5f4eae..2c024c5bf0 100644 --- a/examples/defaultScripts.js +++ b/examples/defaultScripts.js @@ -22,3 +22,4 @@ Script.load("grab.js"); Script.load("directory.js"); Script.load("dialTone.js"); Script.load("attachedEntitiesManager.js"); +Script.load("depthReticle.js"); diff --git a/examples/depthReticle.js b/examples/depthReticle.js index 4b649f49b6..820125a3c7 100644 --- a/examples/depthReticle.js +++ b/examples/depthReticle.js @@ -5,6 +5,8 @@ // Copyright 2016 High Fidelity, Inc. // // When used in HMD, this script will make the reticle depth track to any clickable item in view. +// This script also handles auto-hiding the reticle after inactivity, as well as having the reticle +// seek the look at position upon waking up. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -17,8 +19,63 @@ var desiredDepth = APPARENT_2D_OVERLAY_DEPTH; var TIME_BETWEEN_DEPTH_CHECKS = 100; var MINIMUM_DEPTH_ADJUST = 0.01; var NON_LINEAR_DIVISOR = 2; +var MINIMUM_SEEK_DISTANCE = 0.01; -Script.update.connect(function(deltaTime) { +var lastMouseMove = Date.now(); +var HIDE_STATIC_MOUSE_AFTER = 3000; // 3 seconds +var shouldSeekToLookAt = false; + +Controller.mouseMoveEvent.connect(function(mouseEvent) { + lastMouseMove = Date.now(); + + // if the reticle is hidden, show it... + if (!Reticle.visible) { + Reticle.visible = true; + if (HMD.active) { + shouldSeekToLookAt = true; + } + } +}); + +function seekToLookAt() { + // if we're currently seeking the lookAt move the mouse toward the lookat + if (shouldSeekToLookAt) { + var lookAt2D = HMD.getHUDLookAtPosition2D(); + var currentReticlePosition = Reticle.position; + var distanceBetweenX = lookAt2D.x - Reticle.position.x; + var distanceBetweenY = lookAt2D.y - Reticle.position.y; + var moveX = distanceBetweenX / NON_LINEAR_DIVISOR; + var moveY = distanceBetweenY / NON_LINEAR_DIVISOR; + var newPosition = { x: Reticle.position.x + moveX, y: Reticle.position.y + moveY }; + var closeEnoughX = false; + var closeEnoughY = false; + if (moveX < MINIMUM_SEEK_DISTANCE) { + newPosition.x = lookAt2D.x; + closeEnoughX = true; + } + if (moveY < MINIMUM_SEEK_DISTANCE) { + newPosition.y = lookAt2D.y; + closeEnoughY = true; + } + if (closeEnoughX && closeEnoughY) { + shouldSeekToLookAt = false; + } + Reticle.position = newPosition; + } +} + +function autoHideReticle() { + // if we haven't moved in a long period of time, hide the reticle + if (Reticle.visible) { + var now = Date.now(); + var timeSinceLastMouseMove = now - lastMouseMove; + if (timeSinceLastMouseMove > HIDE_STATIC_MOUSE_AFTER) { + Reticle.visible = false; + } + } +} + +function checkReticleDepth() { var now = Date.now(); var timeSinceLastDepthCheck = now - lastDepthCheckTime; if (timeSinceLastDepthCheck > TIME_BETWEEN_DEPTH_CHECKS) { @@ -56,6 +113,9 @@ Script.update.connect(function(deltaTime) { } } +} + +function moveToDesiredDepth() { // move the reticle toward the desired depth if (desiredDepth != Reticle.depth) { @@ -69,4 +129,13 @@ Script.update.connect(function(deltaTime) { Reticle.setDepth(newDepth); } +} + +Script.update.connect(function(deltaTime) { + autoHideReticle(); // auto hide reticle for desktop or HMD mode + if (HMD.active) { + seekToLookAt(); // handle moving the reticle toward the look at + checkReticleDepth(); // make sure reticle is at correct depth + moveToDesiredDepth(); // move the fade the reticle to the desired depth + } }); From 08eacc436f3d45251c5abb3a18728838e50be87a Mon Sep 17 00:00:00 2001 From: PhilipRosedale Date: Mon, 29 Feb 2016 10:13:01 -0800 Subject: [PATCH 17/21] Fixed rayPlane test again --- examples/edit.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/edit.js b/examples/edit.js index d39a20c2e3..59e1f74d20 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -729,12 +729,12 @@ function rayPlaneIntersection2(pickRay, point, normal) { // var collides = Vec3.dot(pickRay.direction, normal); var d = -Vec3.dot(point, normal); - if (((collides > 0.0) && (d > 0.0)) || ((collides < 0.0) && (d < 0.0))) { - return false; - } var t = -(Vec3.dot(pickRay.origin, normal) + d) / collides; - - return Vec3.sum(pickRay.origin, Vec3.multiply(pickRay.direction, t)); + if (t < 0.0) { + return false; + } else { + return Vec3.sum(pickRay.origin, Vec3.multiply(pickRay.direction, t)); + } } function findClickedEntity(event) { From d1bc31afb64f17001b4c38332c09943f511b50fb Mon Sep 17 00:00:00 2001 From: PhilipRosedale Date: Mon, 29 Feb 2016 10:16:34 -0800 Subject: [PATCH 18/21] azimuth function is really just a ratio --- examples/libraries/entitySelectionTool.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index 1834962e77..1be6030f6e 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -2332,7 +2332,7 @@ SelectionDisplay = (function() { var vector = Vec3.subtract(pick, initialXZPick); // If the mouse is too close to the horizon of the pick plane, stop moving - var MIN_AZIMUTH = 0.02; // Radians + var MIN_AZIMUTH = 0.02; // largest dimension of object divided by distance to it var azimuth = translateXZTool.azimuth(pickRay.origin, pick); if (wantDebug) { print("Start Azimuth: " + translateXZTool.startingAzimuth + ", Azimuth: " + azimuth); From 42f6ede43cee55399012a097abe585244b401e9c Mon Sep 17 00:00:00 2001 From: PhilipRosedale Date: Mon, 29 Feb 2016 10:22:24 -0800 Subject: [PATCH 19/21] azimuth = elevation, duh --- examples/libraries/entitySelectionTool.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index 1be6030f6e..95e695aa9c 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -2263,7 +2263,7 @@ SelectionDisplay = (function() { pickPlanePosition: { x: 0, y: 0, z: 0 }, greatestDimension: 0.0, startingDistance: 0.0, - startingAzimuth: 0.0, + startingElevation: 0.0, onBegin: function(event) { SelectionManager.saveProperties(); startPosition = SelectionManager.worldPosition; @@ -2307,7 +2307,7 @@ SelectionDisplay = (function() { visible: false }); }, - azimuth: function(origin, intersection) { + elevation: function(origin, intersection) { return (origin.y - intersection.y) / Vec3.distance(origin, intersection); }, onMove: function(event) { @@ -2332,13 +2332,13 @@ SelectionDisplay = (function() { var vector = Vec3.subtract(pick, initialXZPick); // If the mouse is too close to the horizon of the pick plane, stop moving - var MIN_AZIMUTH = 0.02; // largest dimension of object divided by distance to it - var azimuth = translateXZTool.azimuth(pickRay.origin, pick); + var MIN_ELEVATION = 0.02; // largest dimension of object divided by distance to it + var elevation = translateXZTool.elevation(pickRay.origin, pick); if (wantDebug) { - print("Start Azimuth: " + translateXZTool.startingAzimuth + ", Azimuth: " + azimuth); + print("Start Elevation: " + translateXZTool.startingElevation + ", elevation: " + elevation); } - if ((translateXZTool.startingAzimuth > 0.0 && azimuth < MIN_AZIMUTH) || - (translateXZTool.startingAzimuth < 0.0 && azimuth > -MIN_AZIMUTH)) { + if ((translateXZTool.startingElevation > 0.0 && elevation < MIN_ELEVATION) || + (translateXZTool.startingElevation < 0.0 && elevation > -MIN_ELEVATION)) { if (wantDebug) { print("too close to horizon!"); } @@ -4150,8 +4150,8 @@ SelectionDisplay = (function() { print("longest dimension: " + translateXZTool.greatestDimension); translateXZTool.startingDistance = Vec3.distance(pickRay.origin, SelectionManager.position); print("starting distance: " + translateXZTool.startingDistance); - translateXZTool.startingAzimuth = translateXZTool.azimuth(pickRay.origin, translateXZTool.pickPlanePosition); - print(" starting azimuth: " + translateXZTool.startingAzimuth); + translateXZTool.startingElevation = translateXZTool.elevation(pickRay.origin, translateXZTool.pickPlanePosition); + print(" starting elevation: " + translateXZTool.startingElevation); } mode = translateXZTool.mode; From bb9d1556c6e148f19449ab0398655da9b8a90085 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 29 Feb 2016 11:07:02 -0800 Subject: [PATCH 20/21] when breaking a parenting loop, make an effort to keep the object near where it was when the loop was created --- libraries/shared/src/SpatiallyNestable.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index 1a897021f1..56532c04f4 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -519,7 +519,13 @@ const Transform SpatiallyNestable::getTransform(int jointIndex, bool& success, i success = false; // someone created a loop. break it... qDebug() << "Parenting loop detected."; - getThisPointer()->setParentID(QUuid()); + SpatiallyNestablePointer _this = getThisPointer(); + _this->setParentID(QUuid()); + bool setPositionSuccess; + AACube aaCube = getQueryAACube(setPositionSuccess); + if (setPositionSuccess) { + _this->setPosition(aaCube.calcCenter()); + } return jointInWorldFrame; } From dc4c4e8c945ff284919e04877d2ffaa5c8a31425 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 29 Feb 2016 14:24:43 -0800 Subject: [PATCH 21/21] some tweaks to auto-hide --- examples/depthReticle.js | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/examples/depthReticle.js b/examples/depthReticle.js index 820125a3c7..14a5ba5ff3 100644 --- a/examples/depthReticle.js +++ b/examples/depthReticle.js @@ -22,11 +22,18 @@ var NON_LINEAR_DIVISOR = 2; var MINIMUM_SEEK_DISTANCE = 0.01; var lastMouseMove = Date.now(); +var lastMouseX = Reticle.position.x; +var lastMouseY = Reticle.position.y; var HIDE_STATIC_MOUSE_AFTER = 3000; // 3 seconds var shouldSeekToLookAt = false; +var fastMouseMoves = 0; +var averageMouseVelocity = 0; +var WEIGHTING = 1/20; // simple moving average over last 20 samples +var ONE_MINUS_WEIGHTING = 1 - WEIGHTING; +var AVERAGE_MOUSE_VELOCITY_FOR_SEEK_TO = 50; Controller.mouseMoveEvent.connect(function(mouseEvent) { - lastMouseMove = Date.now(); + var now = Date.now(); // if the reticle is hidden, show it... if (!Reticle.visible) { @@ -34,12 +41,30 @@ Controller.mouseMoveEvent.connect(function(mouseEvent) { if (HMD.active) { shouldSeekToLookAt = true; } + } else { + // even if the reticle is visible, if we're in HMD mode, and the person is moving their mouse quickly (shaking it) + // then they are probably looking for it, and we should move into seekToLookAt mode + if (HMD.active && !shouldSeekToLookAt) { + var dx = Reticle.position.x - lastMouseX; + var dy = Reticle.position.y - lastMouseY; + var dt = Math.max(1, (now - lastMouseMove)); // mSecs since last mouse move + var mouseMoveDistance = Math.sqrt((dx*dx) + (dy*dy)); + var mouseVelocity = mouseMoveDistance / dt; + averageMouseVelocity = (ONE_MINUS_WEIGHTING * averageMouseVelocity) + (WEIGHTING * mouseVelocity); + if (averageMouseVelocity > AVERAGE_MOUSE_VELOCITY_FOR_SEEK_TO) { + shouldSeekToLookAt = true; + } + } } + lastMouseMove = now; + lastMouseX = mouseEvent.x; + lastMouseY = mouseEvent.y; }); function seekToLookAt() { // if we're currently seeking the lookAt move the mouse toward the lookat if (shouldSeekToLookAt) { + averageMouseVelocity = 0; // reset this, these never count for movement... var lookAt2D = HMD.getHUDLookAtPosition2D(); var currentReticlePosition = Reticle.position; var distanceBetweenX = lookAt2D.x - Reticle.position.x; @@ -57,16 +82,17 @@ function seekToLookAt() { newPosition.y = lookAt2D.y; closeEnoughY = true; } + Reticle.position = newPosition; if (closeEnoughX && closeEnoughY) { shouldSeekToLookAt = false; } - Reticle.position = newPosition; } } function autoHideReticle() { - // if we haven't moved in a long period of time, hide the reticle - if (Reticle.visible) { + // if we haven't moved in a long period of time, and we're not pointing at some + // system overlay (like a window), then hide the reticle + if (Reticle.visible && !Reticle.pointingAtSystemOverlay) { var now = Date.now(); var timeSinceLastMouseMove = now - lastMouseMove; if (timeSinceLastMouseMove > HIDE_STATIC_MOUSE_AFTER) {