From c14433979b1911a51afbbf6cdcfddf30be4379ed Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 16 Dec 2015 15:09:37 -0800 Subject: [PATCH 01/11] fix crash mode when model late to get collision URL --- .../src/RenderableModelEntityItem.cpp | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index b5203ea460..83bfbf7f05 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -385,7 +385,7 @@ Model* RenderableModelEntityItem::getModel(EntityTreeRenderer* renderer) { _needsInitialSimulation = true; } } - + return result; } @@ -398,14 +398,14 @@ void RenderableModelEntityItem::update(const quint64& now) { EntityItemProperties properties; auto extents = _model->getMeshExtents(); properties.setDimensions(extents.maximum - extents.minimum); - + qCDebug(entitiesrenderer) << "Autoresizing:" << (!getName().isEmpty() ? getName() : getModelURL()); QMetaObject::invokeMethod(DependencyManager::get().data(), "editEntity", Qt::QueuedConnection, Q_ARG(QUuid, getEntityItemID()), Q_ARG(EntityItemProperties, properties)); } - + ModelEntityItem::update(now); } @@ -427,7 +427,7 @@ bool RenderableModelEntityItem::findDetailedRayIntersection(const glm::vec3& ori // << precisionPicking; QString extraInfo; - return _model->findRayIntersectionAgainstSubMeshes(origin, direction, distance, + return _model->findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, surfaceNormal, extraInfo, precisionPicking); } @@ -447,24 +447,22 @@ bool RenderableModelEntityItem::isReadyToComputeShape() { ShapeType type = getShapeType(); if (type == SHAPE_TYPE_COMPOUND) { - if (!_model) { + if (!_model || _model->getCollisionURL().isEmpty()) { EntityTreePointer tree = getTree(); if (tree) { QMetaObject::invokeMethod(tree.get(), "callLoader", Qt::QueuedConnection, Q_ARG(EntityItemID, getID())); } - return false; // hmm... + return false; } - assert(!_model->getCollisionURL().isEmpty()); - if (_model->getURL().isEmpty()) { // we need a render geometry with a scale to proceed, so give up. return false; } - + const QSharedPointer collisionNetworkGeometry = _model->getCollisionGeometry(); const QSharedPointer renderNetworkGeometry = _model->getGeometry(); - + if ((collisionNetworkGeometry && collisionNetworkGeometry->isLoaded()) && (renderNetworkGeometry && renderNetworkGeometry->isLoaded())) { // we have both URLs AND both geometries AND they are both fully loaded. From ff83f8fc37081bfb115cb8878ff224235eed4563 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 16 Dec 2015 15:10:30 -0800 Subject: [PATCH 02/11] fix crash mode when leaving domain with handgun --- libraries/physics/src/PhysicalEntitySimulation.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index 40366257df..5a12627abd 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -44,7 +44,7 @@ void PhysicalEntitySimulation::updateEntitiesInternal(const quint64& now) { void PhysicalEntitySimulation::addEntityInternal(EntityItemPointer entity) { assert(entity); - if (entity->shouldBePhysical()) { + if (entity->shouldBePhysical()) { EntityMotionState* motionState = static_cast(entity->getPhysicsInfo()); if (!motionState) { _pendingAdds.insert(entity); @@ -117,6 +117,7 @@ void PhysicalEntitySimulation::clearEntitiesInternal() { _pendingRemoves.clear(); _pendingAdds.clear(); _pendingChanges.clear(); + _outgoingChanges.clear(); } // end EntitySimulation overrides From 45e31ca4422359e6af92ba257fdb20bbaa1469b6 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 16 Dec 2015 15:35:23 -0800 Subject: [PATCH 03/11] fix warning about unused variable --- interface/src/avatar/Avatar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index f8040754d7..6094a0b9fe 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -157,7 +157,7 @@ void Avatar::animateScaleChanges(float deltaTime) { // snap to the end when we get close enough const float MIN_RELATIVE_SCALE_ERROR = 0.03f; - if (fabsf(_targetScale - currentScale) / _targetScale < 0.03f) { + if (fabsf(_targetScale - currentScale) / _targetScale < MIN_RELATIVE_SCALE_ERROR) { animatedScale = _targetScale; } From 0b9bd858a7c1fedecca7b32190155b5038ba6491 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 16 Dec 2015 21:28:27 -0800 Subject: [PATCH 04/11] implement support for pose ScriptEndpoints --- examples/controllers/reticleTests.js | 42 +++++++++++++++++ .../controllers/src/controllers/Pose.cpp | 18 +++++++- .../impl/endpoints/ScriptEndpoint.cpp | 46 ++++++++++++++++++- .../impl/endpoints/ScriptEndpoint.h | 13 ++++++ 4 files changed, 117 insertions(+), 2 deletions(-) diff --git a/examples/controllers/reticleTests.js b/examples/controllers/reticleTests.js index 4c805c8b1c..3392d1b7d9 100644 --- a/examples/controllers/reticleTests.js +++ b/examples/controllers/reticleTests.js @@ -34,6 +34,48 @@ var mappingJSON = { mapping = Controller.parseMapping(JSON.stringify(mappingJSON)); mapping.enable(); + +var MAPPING_NAME = "com.highfidelity.testing.reticleWithHand"; +var mapping2 = Controller.newMapping(MAPPING_NAME); +//mapping2.from(Controller.Standard.RightHand).debug(true).to(Controller.Actions.LeftHand); + +/* +mapping2.from(Controller.Standard.RightHand).peek().to(function(pose) { + print("Controller.Standard.RightHand value:" + JSON.stringify(pose)); + }); +*/ + +var translation = { x: 0, y: 0.1, z: 0 }; +var translationDx = 0.01; +var translationDy = 0.01; +var translationDz = 0.01; +var TRANSLATION_LIMIT = 0.5; +var rotation = Quat.fromPitchYawRollDegrees(45, 0, 45); +mapping2.from(function() { + translation.x = translation.x + translationDx; + translation.y = translation.y + translationDy; + translation.z = translation.z + translationDz; + if ((translation.x > TRANSLATION_LIMIT) || (translation.x < (-1 *TRANSLATION_LIMIT))) { + translationDx = translationDx * -1; + } + if ((translation.y > TRANSLATION_LIMIT) || (translation.y < (-1 *TRANSLATION_LIMIT))) { + translationDy = translationDy * -1; + } + if ((translation.z > TRANSLATION_LIMIT) || (translation.z < (-1 *TRANSLATION_LIMIT))) { + translationDz = translationDz * -1; + } + var pose = { + translation: translation, + rotation: rotation, + velocity: { x: 0, y: 0, z: 0 }, + angularVelocity: { x: 0, y: 0, z: 0, w: 1 } + }; + return pose; +}).debug(true).to(Controller.Standard.LeftHand); + +Controller.enableMapping(MAPPING_NAME); + + Script.scriptEnding.connect(function(){ mapping.disable(); }); diff --git a/libraries/controllers/src/controllers/Pose.cpp b/libraries/controllers/src/controllers/Pose.cpp index 2281fc98ff..2e1d30cff2 100644 --- a/libraries/controllers/src/controllers/Pose.cpp +++ b/libraries/controllers/src/controllers/Pose.cpp @@ -42,7 +42,23 @@ namespace controller { } void Pose::fromScriptValue(const QScriptValue& object, Pose& pose) { - // nothing for now... + bool isValid = true; + auto translation = object.property("translation"); + auto rotation = object.property("rotation"); + auto velocity = object.property("velocity"); + auto angularVelocity = object.property("angularVelocity"); + if (translation.isValid() && + rotation.isValid() && + velocity.isValid() && + angularVelocity.isValid()) { + vec3FromScriptValue(translation, pose.translation); + quatFromScriptValue(rotation, pose.rotation); + vec3FromScriptValue(velocity, pose.velocity); + quatFromScriptValue(angularVelocity, pose.angularVelocity); + pose.valid = true; + } else { + pose.valid = false; + } } } diff --git a/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.cpp b/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.cpp index bb9517b136..3e7fde347e 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.cpp +++ b/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.cpp @@ -10,6 +10,8 @@ #include +#include + using namespace controller; float ScriptEndpoint::peek() const { @@ -23,7 +25,16 @@ void ScriptEndpoint::updateValue() { return; } - _lastValueRead = (float)_callable.call().toNumber(); + QScriptValue result = _callable.call(); + + // If the callable ever returns a non-number, we assume it's a pose + // and start reporting ourselves as a pose. + if (result.isNumber()) { + _lastValueRead = (float)_callable.call().toNumber(); + } else { + Pose::fromScriptValue(result, _lastPoseRead); + _returnPose = true; + } } void ScriptEndpoint::apply(float value, const Pointer& source) { @@ -44,3 +55,36 @@ void ScriptEndpoint::internalApply(float value, int sourceID) { _callable.call(QScriptValue(), QScriptValueList({ QScriptValue(value), QScriptValue(sourceID) })); } + +Pose ScriptEndpoint::peekPose() const { + const_cast(this)->updatePose(); + return _lastPoseRead; +} + +void ScriptEndpoint::updatePose() { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "updatePose", Qt::QueuedConnection); + return; + } + QScriptValue result = _callable.call(); + Pose::fromScriptValue(result, _lastPoseRead); +} + +void ScriptEndpoint::apply(const Pose& newPose, const Pointer& source) { + if (newPose == _lastPoseWritten) { + return; + } + internalApply(newPose, source->getInput().getID()); +} + +void ScriptEndpoint::internalApply(const Pose& newPose, int sourceID) { + _lastPoseWritten = newPose; + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "internalApply", Qt::QueuedConnection, + Q_ARG(const Pose&, newPose), + Q_ARG(int, sourceID)); + return; + } + _callable.call(QScriptValue(), + QScriptValueList({ Pose::toScriptValue(_callable.engine(), newPose), QScriptValue(sourceID) })); +} diff --git a/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.h index 836af721f6..00439f5560 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.h +++ b/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.h @@ -27,13 +27,26 @@ public: virtual float peek() const override; virtual void apply(float newValue, const Pointer& source) override; + + virtual Pose peekPose() const override; + virtual void apply(const Pose& newValue, const Pointer& source) override; + + virtual bool isPose() const override { return _returnPose; } + protected: Q_INVOKABLE void updateValue(); Q_INVOKABLE virtual void internalApply(float newValue, int sourceID); + + Q_INVOKABLE void updatePose(); + Q_INVOKABLE virtual void internalApply(const Pose& newValue, int sourceID); private: QScriptValue _callable; float _lastValueRead { 0.0f }; float _lastValueWritten { 0.0f }; + + bool _returnPose { false }; + Pose _lastPoseRead; + Pose _lastPoseWritten; }; } From 63a63152a48afe98be231bfd653b3c17c3f7cc02 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 17 Dec 2015 11:10:49 -0800 Subject: [PATCH 05/11] add procedural hand pose example --- .../controllers/proceduralHandPoseExample.js | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 examples/controllers/proceduralHandPoseExample.js diff --git a/examples/controllers/proceduralHandPoseExample.js b/examples/controllers/proceduralHandPoseExample.js new file mode 100644 index 0000000000..5d0b10c46a --- /dev/null +++ b/examples/controllers/proceduralHandPoseExample.js @@ -0,0 +1,76 @@ +// +// proceduralHandPoseExample.js +// examples/controllers +// +// Created by Brad Hefta-Gaub on 2015/12/15 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +var MAPPING_NAME = "com.highfidelity.examples.proceduralHandPose"; +var mapping = Controller.newMapping(MAPPING_NAME); +var translation = { x: 0, y: 0.1, z: 0 }; +var translationDx = 0.01; +var translationDy = 0.01; +var translationDz = -0.01; +var TRANSLATION_LIMIT = 0.5; + +var pitch = 45; +var yaw = 0; +var roll = 45; +var pitchDelta = 1; +var yawDelta = -1; +var rollDelta = 1; +var ROTATION_MIN = -90; +var ROTATION_MAX = 90; + +mapping.from(function() { + + // adjust the hand translation in a periodic back and forth motion for each of the 3 axes + translation.x = translation.x + translationDx; + translation.y = translation.y + translationDy; + translation.z = translation.z + translationDz; + if ((translation.x > TRANSLATION_LIMIT) || (translation.x < (-1 * TRANSLATION_LIMIT))) { + translationDx = translationDx * -1; + } + if ((translation.y > TRANSLATION_LIMIT) || (translation.y < (-1 * TRANSLATION_LIMIT))) { + translationDy = translationDy * -1; + } + if ((translation.z > TRANSLATION_LIMIT) || (translation.z < (-1 * TRANSLATION_LIMIT))) { + translationDz = translationDz * -1; + } + + // adjust the hand rotation in a periodic back and forth motion for each of pitch/yaw/roll + pitch = pitch + pitchDelta; + yaw = yaw + yawDelta; + roll = roll + rollDelta; + if ((pitch > ROTATION_MAX) || (pitch < ROTATION_MIN)) { + pitchDelta = pitchDelta * -1; + } + if ((yaw > ROTATION_MAX) || (yaw < ROTATION_MIN)) { + yawDelta = yawDelta * -1; + } + if ((roll > ROTATION_MAX) || (roll < ROTATION_MIN)) { + rollDelta = rollDelta * -1; + } + + var rotation = Quat.fromPitchYawRollDegrees(pitch, yaw, roll); + + var pose = { + translation: translation, + rotation: rotation, + velocity: { x: 0, y: 0, z: 0 }, + angularVelocity: { x: 0, y: 0, z: 0, w: 1 } + }; + return pose; +}).debug(true).to(Controller.Standard.LeftHand); + +Controller.enableMapping(MAPPING_NAME); + + +Script.scriptEnding.connect(function(){ + mapping.disable(); +}); From 762aefbbca611dd51a6c14b7d310738ff50cf8a6 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 17 Dec 2015 17:38:20 -0800 Subject: [PATCH 06/11] add hand driven reticle --- examples/controllers/reticleHandTest.js | 35 +++++++++++++++ examples/controllers/reticleTests.js | 43 ------------------- .../src/controllers/ScriptingInterface.h | 3 ++ 3 files changed, 38 insertions(+), 43 deletions(-) create mode 100644 examples/controllers/reticleHandTest.js diff --git a/examples/controllers/reticleHandTest.js b/examples/controllers/reticleHandTest.js new file mode 100644 index 0000000000..5d0e2f238a --- /dev/null +++ b/examples/controllers/reticleHandTest.js @@ -0,0 +1,35 @@ +// +// reticleTest.js +// examples/controllers +// +// Created by Brad Hefta-Gaub on 2015/12/15 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +function moveReticle(pitch, yaw) { + //print("pitch:" + pitch); + //print("yaw:" + yaw); + + var globalPos = Controller.getReticlePosition(); + globalPos.x += yaw; + globalPos.y += pitch; + Controller.setReticlePosition(globalPos); +} + +var MAPPING_NAME = "com.highfidelity.testing.reticleWithHand"; +var mapping = Controller.newMapping(MAPPING_NAME); +mapping.from(Controller.Standard.RightHand).peek().to(function(pose) { + var angularVelocityEADs = Quat.safeEulerAngles(pose.angularVelocity); // degrees + var yaw = isNaN(angularVelocityEADs.y) ? 0 : (-angularVelocityEADs.y / 10); + var pitch = isNaN(angularVelocityEADs.x) ? 0 : (-angularVelocityEADs.x / 10); + moveReticle(pitch, yaw); +}); +mapping.enable(); + + +Script.scriptEnding.connect(function(){ + mapping.disable(); +}); diff --git a/examples/controllers/reticleTests.js b/examples/controllers/reticleTests.js index 3392d1b7d9..56b2ba413a 100644 --- a/examples/controllers/reticleTests.js +++ b/examples/controllers/reticleTests.js @@ -33,49 +33,6 @@ var mappingJSON = { mapping = Controller.parseMapping(JSON.stringify(mappingJSON)); mapping.enable(); - - -var MAPPING_NAME = "com.highfidelity.testing.reticleWithHand"; -var mapping2 = Controller.newMapping(MAPPING_NAME); -//mapping2.from(Controller.Standard.RightHand).debug(true).to(Controller.Actions.LeftHand); - -/* -mapping2.from(Controller.Standard.RightHand).peek().to(function(pose) { - print("Controller.Standard.RightHand value:" + JSON.stringify(pose)); - }); -*/ - -var translation = { x: 0, y: 0.1, z: 0 }; -var translationDx = 0.01; -var translationDy = 0.01; -var translationDz = 0.01; -var TRANSLATION_LIMIT = 0.5; -var rotation = Quat.fromPitchYawRollDegrees(45, 0, 45); -mapping2.from(function() { - translation.x = translation.x + translationDx; - translation.y = translation.y + translationDy; - translation.z = translation.z + translationDz; - if ((translation.x > TRANSLATION_LIMIT) || (translation.x < (-1 *TRANSLATION_LIMIT))) { - translationDx = translationDx * -1; - } - if ((translation.y > TRANSLATION_LIMIT) || (translation.y < (-1 *TRANSLATION_LIMIT))) { - translationDy = translationDy * -1; - } - if ((translation.z > TRANSLATION_LIMIT) || (translation.z < (-1 *TRANSLATION_LIMIT))) { - translationDz = translationDz * -1; - } - var pose = { - translation: translation, - rotation: rotation, - velocity: { x: 0, y: 0, z: 0 }, - angularVelocity: { x: 0, y: 0, z: 0, w: 1 } - }; - return pose; -}).debug(true).to(Controller.Standard.LeftHand); - -Controller.enableMapping(MAPPING_NAME); - - Script.scriptEnding.connect(function(){ mapping.disable(); }); diff --git a/libraries/controllers/src/controllers/ScriptingInterface.h b/libraries/controllers/src/controllers/ScriptingInterface.h index 9af478e709..9d3942cd55 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.h +++ b/libraries/controllers/src/controllers/ScriptingInterface.h @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -87,6 +88,8 @@ namespace controller { Q_INVOKABLE QObject* parseMapping(const QString& json); Q_INVOKABLE QObject* loadMapping(const QString& jsonUrl); + Q_INVOKABLE glm::vec2 getReticlePosition() { return toGlm(QCursor::pos()); } + Q_INVOKABLE void setReticlePosition(glm::vec2 position) { QCursor::setPos(position.x, position.y); } //Q_INVOKABLE bool isPrimaryButtonPressed() const; //Q_INVOKABLE glm::vec2 getPrimaryJoystickPosition() const; From d344ef6e2a98cc26da192ce81024f805265a683e Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 17 Dec 2015 18:10:41 -0800 Subject: [PATCH 07/11] fix warning in my PR --- libraries/controllers/src/controllers/Pose.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/controllers/src/controllers/Pose.cpp b/libraries/controllers/src/controllers/Pose.cpp index 2e1d30cff2..8f43242431 100644 --- a/libraries/controllers/src/controllers/Pose.cpp +++ b/libraries/controllers/src/controllers/Pose.cpp @@ -42,7 +42,6 @@ namespace controller { } void Pose::fromScriptValue(const QScriptValue& object, Pose& pose) { - bool isValid = true; auto translation = object.property("translation"); auto rotation = object.property("rotation"); auto velocity = object.property("velocity"); From 5701ad2e8cdf4f700938f033442990888c411569 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 18 Dec 2015 12:38:50 -0800 Subject: [PATCH 08/11] update to new angular velocity --- examples/controllers/proceduralHandPoseExample.js | 2 +- libraries/controllers/src/controllers/Pose.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/controllers/proceduralHandPoseExample.js b/examples/controllers/proceduralHandPoseExample.js index 5d0b10c46a..8b735f579f 100644 --- a/examples/controllers/proceduralHandPoseExample.js +++ b/examples/controllers/proceduralHandPoseExample.js @@ -63,7 +63,7 @@ mapping.from(function() { translation: translation, rotation: rotation, velocity: { x: 0, y: 0, z: 0 }, - angularVelocity: { x: 0, y: 0, z: 0, w: 1 } + angularVelocity: { x: 0, y: 0, z: 0 } }; return pose; }).debug(true).to(Controller.Standard.LeftHand); diff --git a/libraries/controllers/src/controllers/Pose.cpp b/libraries/controllers/src/controllers/Pose.cpp index 6b2d12e5df..d9641618c1 100644 --- a/libraries/controllers/src/controllers/Pose.cpp +++ b/libraries/controllers/src/controllers/Pose.cpp @@ -53,7 +53,7 @@ namespace controller { vec3FromScriptValue(translation, pose.translation); quatFromScriptValue(rotation, pose.rotation); vec3FromScriptValue(velocity, pose.velocity); - quatFromScriptValue(angularVelocity, pose.angularVelocity); + vec3FromScriptValue(angularVelocity, pose.angularVelocity); pose.valid = true; } else { pose.valid = false; From 4075f355a3dfa9a0e9e5016de4570b9ab68feedc Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 18 Dec 2015 15:44:01 -0800 Subject: [PATCH 09/11] more work on hand movement --- examples/controllers/reticleHandTest.js | 82 ++++++++++++++++--- interface/src/Application.cpp | 36 ++++++-- .../src/controllers/ScriptingInterface.h | 18 +++- 3 files changed, 117 insertions(+), 19 deletions(-) diff --git a/examples/controllers/reticleHandTest.js b/examples/controllers/reticleHandTest.js index 5d0e2f238a..4132c80d85 100644 --- a/examples/controllers/reticleHandTest.js +++ b/examples/controllers/reticleHandTest.js @@ -9,23 +9,83 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -function moveReticle(pitch, yaw) { - //print("pitch:" + pitch); - //print("yaw:" + yaw); - var globalPos = Controller.getReticlePosition(); - globalPos.x += yaw; - globalPos.y += pitch; - Controller.setReticlePosition(globalPos); +function msecTimestampNow() { + var d = new Date(); + return d.getTime(); } +function length(posA, posB) { + var dx = posA.x - posB.x; + var dy = posA.y - posB.y; + var length = Math.sqrt((dx*dx) + (dy*dy)) + return length; +} + +var EXPECTED_CHANGE = 50; +var lastPos = Controller.getReticlePosition(); +function moveReticle(dX, dY) { + var globalPos = Controller.getReticlePosition(); + + // some debugging to see if position is jumping around on us... + var distanceSinceLastMove = length(lastPos, globalPos); + if (distanceSinceLastMove > EXPECTED_CHANGE) { + print("distanceSinceLastMove:" + distanceSinceLastMove + "----------------------------"); + } + + if (Math.abs(dX) > EXPECTED_CHANGE) { + print("surpressing unexpectedly large change dX:" + dX + "----------------------------"); + dX = 0; + } + if (Math.abs(dY) > EXPECTED_CHANGE) { + print("surpressing unexpectedly large change dY:" + dY + "----------------------------"); + dY = 0; + } + + globalPos.x += dX; + globalPos.y += dY; + Controller.setReticlePosition(globalPos); + lastPos = globalPos; +} + +var lastTime = msecTimestampNow(); + var MAPPING_NAME = "com.highfidelity.testing.reticleWithHand"; var mapping = Controller.newMapping(MAPPING_NAME); mapping.from(Controller.Standard.RightHand).peek().to(function(pose) { - var angularVelocityEADs = Quat.safeEulerAngles(pose.angularVelocity); // degrees - var yaw = isNaN(angularVelocityEADs.y) ? 0 : (-angularVelocityEADs.y / 10); - var pitch = isNaN(angularVelocityEADs.x) ? 0 : (-angularVelocityEADs.x / 10); - moveReticle(pitch, yaw); + + + // pose.angularVelocity - is the angularVelocity in a "physics" sense, that + // means the direction of the vector is the axis of symetry of rotation + // and the scale of the vector is the speed in radians/second of rotation + // around that axis. + // + // we want to deconstruct that in the portion of the rotation on the Y axis + // and make that portion move our reticle in the horizontal/X direction + // and the portion of the rotation on the X axis and make that portion + // move our reticle in the veritcle/Y direction + var xPart = -pose.angularVelocity.y; + var yPart = -pose.angularVelocity.x; + + + var MOVE_SCALE = 1; + var MSECS_PER_SECOND = 1000; + var now = msecTimestampNow(); + var secondsSinceLast = (now - lastTime) / MSECS_PER_SECOND; + lastTime = now; + + //print("secondsSinceLast:" + secondsSinceLast); + + //print("x part:" + xPart); + //print("y part:" + yPart); + + var dX = (xPart * MOVE_SCALE) / secondsSinceLast; + var dY = (yPart * MOVE_SCALE) / secondsSinceLast; + + //print("dX:" + dX); + //print("dY:" + dY); + + moveReticle(dX, dY); }); mapping.enable(); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index fa5565417d..045b8beb40 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -686,13 +686,37 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : } else if (action == controller::toInt(controller::Action::CONTEXT_MENU)) { VrMenu::toggle(); // show context menu even on non-stereo displays } else if (action == controller::toInt(controller::Action::RETICLE_X)) { - auto globalPos = QCursor::pos(); - globalPos.setX(globalPos.x() + state); - QCursor::setPos(globalPos); + auto oldPos = QCursor::pos(); + auto newPos = oldPos; + newPos.setX(oldPos.x() + state); + QCursor::setPos(newPos); + + + // NOTE: This is some debugging code we will leave in while debugging various reticle movement strategies, + // remove it after we're done + const float REASONABLE_CHANGE = 50.0f; + glm::vec2 oldPosG = { oldPos.x(), oldPos.y() }; + glm::vec2 newPosG = { newPos.x(), newPos.y() }; + auto distance = glm::distance(oldPosG, newPosG); + if (distance > REASONABLE_CHANGE) { + qDebug() << "Action::RETICLE_X... UNREASONABLE CHANGE! distance:" << distance << " oldPos:" << oldPosG << " newPos:" << newPosG; + } + } else if (action == controller::toInt(controller::Action::RETICLE_Y)) { - auto globalPos = QCursor::pos(); - globalPos.setY(globalPos.y() + state); - QCursor::setPos(globalPos); + auto oldPos = QCursor::pos(); + auto newPos = oldPos; + newPos.setY(oldPos.y() + state); + QCursor::setPos(newPos); + + // NOTE: This is some debugging code we will leave in while debugging various reticle movement strategies, + // remove it after we're done + const float REASONABLE_CHANGE = 50.0f; + glm::vec2 oldPosG = { oldPos.x(), oldPos.y() }; + glm::vec2 newPosG = { newPos.x(), newPos.y() }; + auto distance = glm::distance(oldPosG, newPosG); + if (distance > REASONABLE_CHANGE) { + qDebug() << "Action::RETICLE_Y... UNREASONABLE CHANGE! distance:" << distance << " oldPos:" << oldPosG << " newPos:" << newPosG; + } } } }); diff --git a/libraries/controllers/src/controllers/ScriptingInterface.h b/libraries/controllers/src/controllers/ScriptingInterface.h index 9d3942cd55..52ec52e852 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.h +++ b/libraries/controllers/src/controllers/ScriptingInterface.h @@ -30,6 +30,7 @@ #include #include +#include #include "UserInputMapper.h" #include "StandardControls.h" @@ -88,8 +89,21 @@ namespace controller { Q_INVOKABLE QObject* parseMapping(const QString& json); Q_INVOKABLE QObject* loadMapping(const QString& jsonUrl); - Q_INVOKABLE glm::vec2 getReticlePosition() { return toGlm(QCursor::pos()); } - Q_INVOKABLE void setReticlePosition(glm::vec2 position) { QCursor::setPos(position.x, position.y); } + Q_INVOKABLE glm::vec2 getReticlePosition() { + return toGlm(QCursor::pos()); + } + Q_INVOKABLE void setReticlePosition(glm::vec2 position) { + // NOTE: This is some debugging code we will leave in while debugging various reticle movement strategies, + // remove it after we're done + const float REASONABLE_CHANGE = 50.0f; + glm::vec2 oldPos = toGlm(QCursor::pos()); + auto distance = glm::distance(oldPos, position); + if (distance > REASONABLE_CHANGE) { + qDebug() << "Contrller::ScriptingInterface ---- UNREASONABLE CHANGE! distance:" << distance << " oldPos:" << oldPos << " newPos:" << position; + } + + QCursor::setPos(position.x, position.y); + } //Q_INVOKABLE bool isPrimaryButtonPressed() const; //Q_INVOKABLE glm::vec2 getPrimaryJoystickPosition() const; From a621579f17043ee5db822f183e50a6f1a017ed94 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 18 Dec 2015 15:44:21 -0800 Subject: [PATCH 10/11] add philips version of hand movement --- examples/controllers/philipsVersion.js | 87 ++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 examples/controllers/philipsVersion.js diff --git a/examples/controllers/philipsVersion.js b/examples/controllers/philipsVersion.js new file mode 100644 index 0000000000..4ae617cf0b --- /dev/null +++ b/examples/controllers/philipsVersion.js @@ -0,0 +1,87 @@ +// +// reticleTest.js +// examples/controllers +// +// Created by Brad Hefta-Gaub on 2015/12/15 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +function length(posA, posB) { + var dx = posA.x - posB.x; + var dy = posA.y - posB.y; + var length = Math.sqrt((dx*dx) + (dy*dy)) + return length; +} + +var PITCH_DEADZONE = 1.0; +var PITCH_MAX = 20.0; +var YAW_DEADZONE = 1.0; +var YAW_MAX = 20.0; +var PITCH_SCALING = 10.0; +var YAW_SCALING = 10.0; + +var EXPECTED_CHANGE = 50; +var lastPos = Controller.getReticlePosition(); +function moveReticle(dY, dX) { + var globalPos = Controller.getReticlePosition(); + + // some debugging to see if position is jumping around on us... + var distanceSinceLastMove = length(lastPos, globalPos); + if (distanceSinceLastMove > EXPECTED_CHANGE) { + print("distanceSinceLastMove:" + distanceSinceLastMove + "----------------------------"); + } + + if (Math.abs(dX) > EXPECTED_CHANGE) { + print("UNEXPECTED dX:" + dX + "----------------------------"); + dX = 0; + } + if (Math.abs(dY) > EXPECTED_CHANGE) { + print("UNEXPECTED dY:" + dY + "----------------------------"); + dY = 0; + } + + globalPos.x += dX; + globalPos.y += dY; + Controller.setReticlePosition(globalPos); + lastPos = globalPos; +} + + +var MAPPING_NAME = "com.highfidelity.testing.reticleWithHand"; +var mapping = Controller.newMapping(MAPPING_NAME); + +var lastHandPitch = 0; +var lastHandYaw = 0; + +mapping.from(Controller.Standard.LeftHand).peek().to(function(pose) { + var handEulers = Quat.safeEulerAngles(pose.rotation); + //Vec3.print("handEulers:", handEulers); + + var handPitch = handEulers.y; + var handYaw = handEulers.x; + var changePitch = (handPitch - lastHandPitch) * PITCH_SCALING; + var changeYaw = (handYaw - lastHandYaw) * YAW_SCALING; + if (Math.abs(changePitch) > PITCH_MAX) { + print("Pitch: " + changePitch); + changePitch = 0; + } + if (Math.abs(changeYaw) > YAW_MAX) { + print("Yaw: " + changeYaw); + changeYaw = 0; + } + changePitch = Math.abs(changePitch) < PITCH_DEADZONE ? 0 : changePitch; + changeYaw = Math.abs(changeYaw) < YAW_DEADZONE ? 0 : changeYaw; + moveReticle(changePitch, changeYaw); + lastHandPitch = handPitch; + lastHandYaw = handYaw; + +}); +mapping.enable(); + + +Script.scriptEnding.connect(function(){ + mapping.disable(); +}); From e929e3f789f94b4305e3801c1a3fb74755b6d3c0 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 18 Dec 2015 19:18:39 -0800 Subject: [PATCH 11/11] more work on angular velocity tests --- ...t.js => reticleHandAngularVelocityTest.js} | 60 +++++++++++++------ libraries/script-engine/src/Quat.cpp | 4 ++ libraries/script-engine/src/Quat.h | 1 + 3 files changed, 48 insertions(+), 17 deletions(-) rename examples/controllers/{reticleHandTest.js => reticleHandAngularVelocityTest.js} (57%) diff --git a/examples/controllers/reticleHandTest.js b/examples/controllers/reticleHandAngularVelocityTest.js similarity index 57% rename from examples/controllers/reticleHandTest.js rename to examples/controllers/reticleHandAngularVelocityTest.js index 4132c80d85..94288b7bfb 100644 --- a/examples/controllers/reticleHandTest.js +++ b/examples/controllers/reticleHandAngularVelocityTest.js @@ -1,5 +1,5 @@ // -// reticleTest.js +// reticleHandAngularVelocityTest.js // examples/controllers // // Created by Brad Hefta-Gaub on 2015/12/15 @@ -9,6 +9,14 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +// If you set this to true, you will get the raw instantaneous angular velocity. +// note: there is a LOT of noise in the hydra rotation, you will probably be very +// frustrated with the level of jitter. +var USE_INSTANTANEOUS_ANGULAR_VELOCITY = false; +var whichHand = Controller.Standard.RightHand; +var whichTrigger = Controller.Standard.RT; + + function msecTimestampNow() { var d = new Date(); @@ -30,7 +38,7 @@ function moveReticle(dX, dY) { // some debugging to see if position is jumping around on us... var distanceSinceLastMove = length(lastPos, globalPos); if (distanceSinceLastMove > EXPECTED_CHANGE) { - print("distanceSinceLastMove:" + distanceSinceLastMove + "----------------------------"); + print("------------------ distanceSinceLastMove:" + distanceSinceLastMove + "----------------------------"); } if (Math.abs(dX) > EXPECTED_CHANGE) { @@ -48,12 +56,25 @@ function moveReticle(dX, dY) { lastPos = globalPos; } +var firstTime = true; var lastTime = msecTimestampNow(); +var previousRotation; var MAPPING_NAME = "com.highfidelity.testing.reticleWithHand"; var mapping = Controller.newMapping(MAPPING_NAME); -mapping.from(Controller.Standard.RightHand).peek().to(function(pose) { +mapping.from(whichTrigger).peek().constrainToInteger().to(Controller.Actions.ReticleClick); +mapping.from(whichHand).peek().to(function(pose) { + var MSECS_PER_SECOND = 1000; + var now = msecTimestampNow(); + var deltaMsecs = (now - lastTime); + var deltaTime = deltaMsecs / MSECS_PER_SECOND; + + if (firstTime) { + previousRotation = pose.rotation; + lastTime = msecTimestampNow(); + firstTime = false; + } // pose.angularVelocity - is the angularVelocity in a "physics" sense, that // means the direction of the vector is the axis of symetry of rotation @@ -67,29 +88,34 @@ mapping.from(Controller.Standard.RightHand).peek().to(function(pose) { var xPart = -pose.angularVelocity.y; var yPart = -pose.angularVelocity.x; + // pose.angularVelocity is "smoothed", we can calculate our own instantaneous + // angular velocity as such: + if (USE_INSTANTANEOUS_ANGULAR_VELOCITY) { + var previousConjugate = Quat.conjugate(previousRotation); + var deltaRotation = Quat.multiply(pose.rotation, previousConjugate); + var normalizedDeltaRotation = Quat.normalize(deltaRotation); + var axis = Quat.axis(normalizedDeltaRotation); + var speed = Quat.angle(normalizedDeltaRotation) / deltaTime; + var instantaneousAngularVelocity = Vec3.multiply(speed, axis); + + xPart = -instantaneousAngularVelocity.y; + yPart = -instantaneousAngularVelocity.x; + + previousRotation = pose.rotation; + } var MOVE_SCALE = 1; - var MSECS_PER_SECOND = 1000; - var now = msecTimestampNow(); - var secondsSinceLast = (now - lastTime) / MSECS_PER_SECOND; lastTime = now; - //print("secondsSinceLast:" + secondsSinceLast); - - //print("x part:" + xPart); - //print("y part:" + yPart); - - var dX = (xPart * MOVE_SCALE) / secondsSinceLast; - var dY = (yPart * MOVE_SCALE) / secondsSinceLast; - - //print("dX:" + dX); - //print("dY:" + dY); + var dX = (xPart * MOVE_SCALE) / deltaTime; + var dY = (yPart * MOVE_SCALE) / deltaTime; moveReticle(dX, dY); }); mapping.enable(); - Script.scriptEnding.connect(function(){ mapping.disable(); }); + + diff --git a/libraries/script-engine/src/Quat.cpp b/libraries/script-engine/src/Quat.cpp index bb74b20be0..f6b1726770 100644 --- a/libraries/script-engine/src/Quat.cpp +++ b/libraries/script-engine/src/Quat.cpp @@ -22,6 +22,10 @@ quat Quat::normalize(const glm::quat& q) { return glm::normalize(q); } +quat Quat::conjugate(const glm::quat& q) { + return glm::conjugate(q); +} + glm::quat Quat::rotationBetween(const glm::vec3& v1, const glm::vec3& v2) { return ::rotationBetween(v1, v2); } diff --git a/libraries/script-engine/src/Quat.h b/libraries/script-engine/src/Quat.h index 543c93401f..bc7f11ab01 100644 --- a/libraries/script-engine/src/Quat.h +++ b/libraries/script-engine/src/Quat.h @@ -26,6 +26,7 @@ class Quat : public QObject { public slots: glm::quat multiply(const glm::quat& q1, const glm::quat& q2); glm::quat normalize(const glm::quat& q); + glm::quat conjugate(const glm::quat& q); glm::quat lookAt(const glm::vec3& eye, const glm::vec3& center, const glm::vec3& up); glm::quat lookAtSimple(const glm::vec3& eye, const glm::vec3& center); glm::quat rotationBetween(const glm::vec3& v1, const glm::vec3& v2);