From 20d95080f1c0a6a252e1ee7e4364451c93db672c Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 5 Nov 2015 12:03:45 -0800 Subject: [PATCH 1/8] IK fix for avatars exported from Blender This should fix the issue with the hips moving erratically when arm IK is enabled. The main issue is that the IK system assumed that the "Hips" joint was the root of the skeleton. For Blender avatar this is not the case as it inserts an "Armature" node at the root instead. --- libraries/animation/src/AnimInverseKinematics.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index 42e9472819..6355426dae 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -156,11 +156,11 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vectorgetParentIndex(tipIndex); - if (pivotIndex == -1) { + if (pivotIndex == -1 || pivotIndex == _hipsIndex) { continue; } int pivotsParentIndex = _skeleton->getParentIndex(pivotIndex); - if (pivotsParentIndex == -1) { + if (pivotsParentIndex == -1 || pivotIndex == _hipsIndex) { // TODO?: handle case where tip's parent is root? continue; } @@ -173,7 +173,7 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vectorgetParentIndex(i); - if (parentIndex != -1) { + if (parentIndex != -1 && parentIndex != _hipsIndex) { absolutePoses[i] = absolutePoses[parentIndex] * _relativePoses[i]; } } @@ -295,7 +295,7 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vectorgetParentIndex(tipIndex); - if (parentIndex != -1) { + if (parentIndex != -1 && parentIndex != _hipsIndex) { const glm::quat& targetRotation = target.getRotation(); // compute tip's new parent-relative rotation // Q = Qp * q --> q' = Qp^ * Q From c6b42f5fbc4cbcdd3a75969ddd7a4243493c0a1b Mon Sep 17 00:00:00 2001 From: AlessandroSigna Date: Thu, 5 Nov 2015 18:48:24 -0800 Subject: [PATCH 2/8] Added a second way to create mapping --- examples/controllers/controllerMappings.js | 68 +++++++++++++--------- examples/controllers/rightClickExample.js | 10 ---- 2 files changed, 39 insertions(+), 39 deletions(-) delete mode 100644 examples/controllers/rightClickExample.js diff --git a/examples/controllers/controllerMappings.js b/examples/controllers/controllerMappings.js index a44833f80a..1bcd05a4cc 100644 --- a/examples/controllers/controllerMappings.js +++ b/examples/controllers/controllerMappings.js @@ -4,68 +4,74 @@ // examples // // Created by Sam Gondelman on 6/2/15 +// Rewritten by Alessandro Signa on 11/05/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 // -// This allows to change the input mapping making a sense of how the new mapping works +// This allows to change the input mapping to easly understand of how the new mapping works. +// Two different ways are presented: the first one uses a JSON file to create the mapping, the second one declares the new routes explicitly one at a time. +// You shuold prefer the first method if you have a lot of new routes, the second one if you want to express the action as a function. /* -This function returns a JSON body. It's in charge to modify the mouse/keyboard mapping. The keyboard inputs are mapped directly to actions. +This function returns a JSON body. It's in charge to modify the standard controller and the mouse/keyboard mapping. + +The Standard controller is an abstraction: all the actual controllers are mapped to it. (i.e. Hydra --mapped to-> Standard --mapped to-> Action) +This example will overwrite the mapping of the left axis (Standard.LY, Standard.LX). +It's possible to find all the standard inputs (and their mapping) into standard.json +To try these changes you need a controller, not the keyboard. + +The keyboard/mouse inputs are mapped directly to actions since the keyboard doesn't have its default mapping passing through the Standard controller. If this new mapping contains inputs which are defined in the standard mapping, these will overwrite the old ones(Keyboard.W, Keyboard.RightMouseButton). If this new mapping contains inputs which are not defined in the standard, these will be added to the mapping(Keyboard.M). */ myFirstMapping = function() { return { - "name": "example", + "name": "controllerMapping_First", "channels": [ - { "from": "Keyboard.W", "to": "Actions.YAW_LEFT" }, - { "from": "Keyboard.M", "to": "Actions.YAW_LEFT" }, - - { "from": "Keyboard.RightMouseButton", "to": "Actions.YAW_RIGHT" } - ] -} -} -/* -This JSON is in charge to modify the Standard controller mapping. -The standard controller is an abstraction: all the actual controllers are mapped to it. (i.e. Hydra --mapped to-> Standard --mapped to-> Action) -These new inputs will overwrite the standard ones. -It's possible to find all the standard inputs (and their mapping) into standard.json -To try the mySecondMapping effect you need a controller, not the keyboard. -*/ -mySecondMapping = function() { -return { - "name": "example2", - "channels": [ { "from": "Standard.LY", "to": "Actions.Yaw" }, { "from": "Standard.LX", "to": "Actions.Yaw" }, + + { "from": "Keyboard.W", "to": "Actions.YAW_LEFT" }, + { "from": "Keyboard.M", "to": "Actions.YAW_RIGHT" }, + + { "from": "Keyboard.LeftMouseButton", "to": "Actions.Up" } + ] } } -var tryFirst = true; + +var firstWay = true; var mapping; -if(tryFirst){ +var MAPPING_NAME; + + +if(firstWay){ var myFirstMappingJSON = myFirstMapping(); print('myfirstMappingJSON' + JSON.stringify(myFirstMappingJSON)); mapping = Controller.parseMapping(JSON.stringify(myFirstMappingJSON)); + mapping.enable(); }else{ - var mySecondMappingJSON = mySecondMapping(); - print('mySecondMappingJSON' + JSON.stringify(mySecondMappingJSON)); - mapping = Controller.parseMapping(JSON.stringify(mySecondMappingJSON)); + MAPPING_NAME = "controllerMapping_Second"; + var mapping2 = Controller.newMapping(MAPPING_NAME); + mapping2.from(Controller.Hardware.Keyboard.RightMouseClicked).to(function (value) { + print("Keyboard.RightMouseClicked"); + }); + mapping2.from(Controller.Standard.LX).to(Controller.Actions.Yaw); + Controller.enableMapping(MAPPING_NAME); } -mapping.enable(); /* -//-----------------some info prints----------------------- +//-----------------some info prints that you would like to enable----------------------- Object.keys(Controller.Standard).forEach(function (input) { print("Controller.Standard." + input + ":" + Controller.Standard[input]); }); @@ -94,5 +100,9 @@ Controller.hardwareChanged.connect(function () { Script.scriptEnding.connect(function () { - mapping.disable(); + if(firstWay){ + mapping.disable(); + } else { + Controller.disableMapping(MAPPING_NAME); + } }); \ No newline at end of file diff --git a/examples/controllers/rightClickExample.js b/examples/controllers/rightClickExample.js deleted file mode 100644 index c3e6ea8f3d..0000000000 --- a/examples/controllers/rightClickExample.js +++ /dev/null @@ -1,10 +0,0 @@ -var MAPPING_NAME = "com.highfidelity.rightClickExample"; -var mapping = Controller.newMapping(MAPPING_NAME); -mapping.from(Controller.Hardware.Keyboard.RightMouseClicked).to(function (value) { - print("Keyboard.RightMouseClicked"); -}); -Controller.enableMapping(MAPPING_NAME); - -Script.scriptEnding.connect(function () { - Controller.disableMapping(MAPPING_NAME); -}); \ No newline at end of file From 4ed8a1e5d116a663bdf3e0642e21a239548999d1 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 6 Nov 2015 09:25:53 -0800 Subject: [PATCH 3/8] changes to send an update to entity-server when AvatarActionHold releases an entity --- interface/src/avatar/AvatarActionHold.h | 2 +- libraries/entities/src/EntityTree.cpp | 18 ++++++++++++++++-- libraries/entities/src/EntityTree.h | 1 + libraries/physics/src/EntityMotionState.cpp | 11 ++++++++--- libraries/physics/src/EntityMotionState.h | 2 +- 5 files changed, 27 insertions(+), 7 deletions(-) diff --git a/interface/src/avatar/AvatarActionHold.h b/interface/src/avatar/AvatarActionHold.h index 6badf97e9e..95623d5046 100644 --- a/interface/src/avatar/AvatarActionHold.h +++ b/interface/src/avatar/AvatarActionHold.h @@ -30,7 +30,7 @@ public: QByteArray serialize() const; virtual void deserialize(QByteArray serializedArguments); - virtual bool shouldSuppressLocationEdits() { return true; } + virtual bool shouldSuppressLocationEdits() { return _active && !_ownerEntity.expired(); } private: static const uint16_t holdVersion; diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 24c13ae28e..8e32158362 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -611,6 +611,16 @@ EntityItemPointer EntityTree::findEntityByEntityItemID(const EntityItemID& entit return foundEntity; } +void EntityTree::fixupTerseEditLogging(EntityItemProperties& properties, QList& changedProperties) { + if (properties.simulationOwnerChanged()) { + int simIndex = changedProperties.indexOf("simulationOwner"); + if (simIndex >= 0) { + SimulationOwner simOwner = properties.getSimulationOwner(); + changedProperties[simIndex] = QString("simulationOwner:") + QString::number((int)simOwner.getPriority()); + } + } +} + int EntityTree::processEditPacketData(NLPacket& packet, const unsigned char* editData, int maxLength, const SharedNodePointer& senderNode) { @@ -661,7 +671,9 @@ int EntityTree::processEditPacketData(NLPacket& packet, const unsigned char* edi qCDebug(entities) << " properties:" << properties; } if (wantTerseEditLogging()) { - qCDebug(entities) << "edit" << entityItemID.toString() << properties.listChangedProperties(); + QList changedProperties = properties.listChangedProperties(); + fixupTerseEditLogging(properties, changedProperties); + qCDebug(entities) << "edit" << entityItemID.toString() << changedProperties; } endLogging = usecTimestampNow(); @@ -689,7 +701,9 @@ int EntityTree::processEditPacketData(NLPacket& packet, const unsigned char* edi qCDebug(entities) << " properties:" << properties; } if (wantTerseEditLogging()) { - qCDebug(entities) << "add" << entityItemID.toString() << properties.listChangedProperties(); + QList changedProperties = properties.listChangedProperties(); + fixupTerseEditLogging(properties, changedProperties); + qCDebug(entities) << "add" << entityItemID.toString() << changedProperties; } endLogging = usecTimestampNow(); diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 1957787a60..c177840199 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -77,6 +77,7 @@ public: virtual bool canProcessVersion(PacketVersion thisVersion) const { return thisVersion >= VERSION_ENTITIES_USE_METERS_AND_RADIANS; } virtual bool handlesEditPacketType(PacketType packetType) const; + void fixupTerseEditLogging(EntityItemProperties& properties, QList& changedProperties); virtual int processEditPacketData(NLPacket& packet, const unsigned char* editData, int maxLength, const SharedNodePointer& senderNode); diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 42aaea33c2..c8a0f87b6d 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -79,8 +79,13 @@ EntityMotionState::~EntityMotionState() { assert(!_entity); } -void EntityMotionState::updateServerPhysicsVariables() { +void EntityMotionState::updateServerPhysicsVariables(const QUuid& sessionID) { assert(entityTreeIsLocked()); + if (_entity->getSimulatorID() == sessionID) { + // don't slam these values if we are the simulation owner + return; + } + _serverPosition = _entity->getPosition(); _serverRotation = _entity->getRotation(); _serverVelocity = _entity->getVelocity(); @@ -92,7 +97,7 @@ void EntityMotionState::updateServerPhysicsVariables() { // virtual bool EntityMotionState::handleEasyChanges(uint32_t flags, PhysicsEngine* engine) { assert(entityTreeIsLocked()); - updateServerPhysicsVariables(); + updateServerPhysicsVariables(engine->getSessionID()); ObjectMotionState::handleEasyChanges(flags, engine); if (flags & Simulation::DIRTY_SIMULATOR_ID) { @@ -129,7 +134,7 @@ bool EntityMotionState::handleEasyChanges(uint32_t flags, PhysicsEngine* engine) // virtual bool EntityMotionState::handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* engine) { - updateServerPhysicsVariables(); + updateServerPhysicsVariables(engine->getSessionID()); return ObjectMotionState::handleHardAndEasyChanges(flags, engine); } diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index f3a2e80070..188e7096b9 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -28,7 +28,7 @@ public: EntityMotionState(btCollisionShape* shape, EntityItemPointer item); virtual ~EntityMotionState(); - void updateServerPhysicsVariables(); + void updateServerPhysicsVariables(const QUuid& sessionID); virtual bool handleEasyChanges(uint32_t flags, PhysicsEngine* engine); virtual bool handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* engine); From 6d3a79af83e754c107a2f6b488b805395bd0cd2c Mon Sep 17 00:00:00 2001 From: AlessandroSigna Date: Fri, 6 Nov 2015 12:46:08 -0800 Subject: [PATCH 4/8] changed the behaviour of Standard.LY input --- interface/resources/controllers/standard.json | 13 +++++++++++-- interface/src/Application.cpp | 3 +++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/interface/resources/controllers/standard.json b/interface/resources/controllers/standard.json index 5de188c0b6..8a24543b74 100644 --- a/interface/resources/controllers/standard.json +++ b/interface/resources/controllers/standard.json @@ -14,8 +14,17 @@ ] }, { "from": "Standard.RX", "to": "Actions.Yaw" }, - { "from": "Standard.RY", "when": "!Application.InHMD", "to": "Actions.Pitch" }, - + + { "from": "Standard.RY", + "when": "Application.Grounded", + "to": "Actions.Up", + "filters": + [ + { "type": "deadZone", "min": 0.95 }, + "invert" + ] + }, + { "from": "Standard.RY", "to": "Actions.Up", "filters": "invert"}, { "from": [ "Standard.DU", "Standard.DL", "Standard.DR", "Standard.DD" ], "to": "Standard.LeftPrimaryThumb" }, { "from": "Standard.Back", "to": "Standard.LeftSecondaryThumb" }, diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 718d06c6e5..571c6ca6af 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -646,6 +646,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : _applicationStateDevice->addInputVariant(QString("ComfortMode"), controller::StateController::ReadLambda([]() -> float { return (float)Menu::getInstance()->isOptionChecked(MenuOption::ComfortMode); })); + _applicationStateDevice->addInputVariant(QString("Grounded"), controller::StateController::ReadLambda([]() -> float { + return (float)qApp->getMyAvatar()->getCharacterController()->onGround(); + })); userInputMapper->registerDevice(_applicationStateDevice); From 1d41ef868b620e8e12aecc8a76deaaddbd7a5a3c Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 6 Nov 2015 13:11:06 -0800 Subject: [PATCH 5/8] update bubblewand model refs --- examples/toybox/bubblewand/createWand.js | 11 ++++++++--- examples/toybox/bubblewand/wand.js | 2 +- unpublishedScripts/hiddenEntityReset.js | 5 ++--- unpublishedScripts/masterReset.js | 4 ++-- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/examples/toybox/bubblewand/createWand.js b/examples/toybox/bubblewand/createWand.js index 25649a9aad..d62c2064cf 100644 --- a/examples/toybox/bubblewand/createWand.js +++ b/examples/toybox/bubblewand/createWand.js @@ -13,13 +13,18 @@ Script.include("../../utilities.js"); Script.include("../../libraries/utils.js"); -var WAND_MODEL = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/wand.fbx'; -var WAND_COLLISION_SHAPE = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/collisionHull.obj'; +var WAND_MODEL = 'http://hifi-public.s3.amazonaws.com/models/bubblewand/wand.fbx'; +var WAND_COLLISION_SHAPE = 'http://hifi-public.s3.amazonaws.com/models/bubblewand/actual_no_top_collision_hull.obj'; + var WAND_SCRIPT_URL = Script.resolvePath("wand.js"); //create the wand in front of the avatar -var center = Vec3.sum(Vec3.sum(MyAvatar.position, {x: 0, y: 0.5, z: 0}), Vec3.multiply(0.5, Quat.getFront(Camera.getOrientation()))); +var center = Vec3.sum(Vec3.sum(MyAvatar.position, { + x: 0, + y: 0.5, + z: 0 +}), Vec3.multiply(0.5, Quat.getFront(Camera.getOrientation()))); var wand = Entities.addEntity({ name: 'Bubble Wand', diff --git a/examples/toybox/bubblewand/wand.js b/examples/toybox/bubblewand/wand.js index 707b0fd47f..c8ba51f51d 100644 --- a/examples/toybox/bubblewand/wand.js +++ b/examples/toybox/bubblewand/wand.js @@ -17,7 +17,7 @@ Script.include("../../utilities.js"); Script.include("../../libraries/utils.js"); - var BUBBLE_MODEL = "http://hifi-public.s3.amazonaws.com/james/bubblewand/models/bubble/bubble.fbx"; + var BUBBLE_MODEL = "http://hifi-public.s3.amazonaws.com/models/bubblewand/bubble.fbx"; var BUBBLE_INITIAL_DIMENSIONS = { x: 0.01, diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index d343c52497..ef0557a54f 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -927,9 +927,8 @@ } function createWand(position) { - var WAND_MODEL = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/wand.fbx'; - var WAND_COLLISION_SHAPE = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/actual_no_top_collision_hull.obj'; - + var WAND_MODEL = 'http://hifi-public.s3.amazonaws.com/models/bubblewand/wand.fbx'; + var WAND_COLLISION_SHAPE = 'http://hifi-public.s3.amazonaws.com/models/bubblewand/actual_no_top_collision_hull.obj'; var entity = Entities.addEntity({ name: 'Bubble Wand', type: "Model", diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index 364807f42f..a8e156617a 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -909,8 +909,8 @@ MasterReset = function() { } function createWand(position) { - var WAND_MODEL = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/wand.fbx'; - var WAND_COLLISION_SHAPE = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/actual_no_top_collision_hull.obj'; + var WAND_MODEL = 'http://hifi-public.s3.amazonaws.com/models/bubblewand/wand.fbx'; + var WAND_COLLISION_SHAPE = 'http://hifi-public.s3.amazonaws.com/models/bubblewand/actual_no_top_collision_hull.obj'; var entity = Entities.addEntity({ name: 'Bubble Wand', From ced1a4899de4edc4edd1f7793bcc7c0b457ebd5f Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 6 Nov 2015 15:10:23 -0800 Subject: [PATCH 6/8] promises --- examples/libraries/promise.js | 222 +++++++++++++++++++++++++++ examples/libraries/promiseExample.js | 18 +++ 2 files changed, 240 insertions(+) create mode 100644 examples/libraries/promise.js create mode 100644 examples/libraries/promiseExample.js diff --git a/examples/libraries/promise.js b/examples/libraries/promise.js new file mode 100644 index 0000000000..cffa294715 --- /dev/null +++ b/examples/libraries/promise.js @@ -0,0 +1,222 @@ +// Copyright (c) 2014 Taylor Hakes +// Copyright (c) 2014 Forbes Lindesay + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + + + +function promiseMaker() { + + // Use polyfill for setImmediate for performance gains + var asap = (typeof setImmediate === 'function' && setImmediate) || + function(fn) { + Script.setTimeout(fn, 1); + }; + + // Polyfill for Function.prototype.bind + function bind(fn, thisArg) { + return function() { + fn.apply(thisArg, arguments); + } + } + + var isArray = Array.isArray || function(value) { + return Object.prototype.toString.call(value) === "[object Array]" + }; + + function Promise(fn) { + if (typeof this !== 'object') throw new TypeError('Promises must be constructed via new'); + if (typeof fn !== 'function') throw new TypeError('not a function'); + this._state = null; + this._value = null; + this._deferreds = [] + + doResolve(fn, bind(resolve, this), bind(reject, this)) + } + + function handle(deferred) { + var me = this; + if (this._state === null) { + this._deferreds.push(deferred); + return + } + asap(function() { + var cb = me._state ? deferred.onFulfilled : deferred.onRejected + if (cb === null) { + (me._state ? deferred.resolve : deferred.reject)(me._value); + return; + } + var ret; + try { + ret = cb(me._value); + } catch (e) { + deferred.reject(e); + return; + } + deferred.resolve(ret); + }) + } + + function resolve(newValue) { + try { //Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure + if (newValue === this) throw new TypeError('A promise cannot be resolved with itself.'); + if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) { + var then = newValue.then; + if (typeof then === 'function') { + doResolve(bind(then, newValue), bind(resolve, this), bind(reject, this)); + return; + } + } + this._state = true; + this._value = newValue; + finale.call(this); + } catch (e) { + reject.call(this, e); + } + } + + function reject(newValue) { + this._state = false; + this._value = newValue; + finale.call(this); + } + + function finale() { + for (var i = 0, len = this._deferreds.length; i < len; i++) { + handle.call(this, this._deferreds[i]); + } + this._deferreds = null; + } + + function Handler(onFulfilled, onRejected, resolve, reject) { + this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null; + this.onRejected = typeof onRejected === 'function' ? onRejected : null; + this.resolve = resolve; + this.reject = reject; + } + + /** + * Take a potentially misbehaving resolver function and make sure + * onFulfilled and onRejected are only called once. + * + * Makes no guarantees about asynchrony. + */ + function doResolve(fn, onFulfilled, onRejected) { + var done = false; + try { + fn(function(value) { + if (done) return; + done = true; + onFulfilled(value); + }, function(reason) { + if (done) return; + done = true; + onRejected(reason); + }) + } catch (ex) { + if (done) return; + done = true; + onRejected(ex); + } + } + + Promise.prototype['catch'] = function(onRejected) { + return this.then(null, onRejected); + }; + + Promise.prototype.then = function(onFulfilled, onRejected) { + var me = this; + return new Promise(function(resolve, reject) { + handle.call(me, new Handler(onFulfilled, onRejected, resolve, reject)); + }) + }; + + Promise.all = function() { + var args = Array.prototype.slice.call(arguments.length === 1 && isArray(arguments[0]) ? arguments[0] : arguments); + + return new Promise(function(resolve, reject) { + if (args.length === 0) return resolve([]); + var remaining = args.length; + + function res(i, val) { + try { + if (val && (typeof val === 'object' || typeof val === 'function')) { + var then = val.then; + if (typeof then === 'function') { + then.call(val, function(val) { + res(i, val) + }, reject); + return; + } + } + args[i] = val; + if (--remaining === 0) { + resolve(args); + } + } catch (ex) { + reject(ex); + } + } + for (var i = 0; i < args.length; i++) { + res(i, args[i]); + } + }); + }; + + Promise.resolve = function(value) { + if (value && typeof value === 'object' && value.constructor === Promise) { + return value; + } + + return new Promise(function(resolve) { + resolve(value); + }); + }; + + Promise.reject = function(value) { + return new Promise(function(resolve, reject) { + reject(value); + }); + }; + + Promise.race = function(values) { + return new Promise(function(resolve, reject) { + for (var i = 0, len = values.length; i < len; i++) { + values[i].then(resolve, reject); + } + }); + }; + + /** + * Set the immediate function to execute callbacks + * @param fn {function} Function to execute + * @private + */ + Promise._setImmediateFn = function _setImmediateFn(fn) { + asap = fn; + }; + + + return Promise + +} + +loadPromise = function() { + return promiseMaker(); +} \ No newline at end of file diff --git a/examples/libraries/promiseExample.js b/examples/libraries/promiseExample.js new file mode 100644 index 0000000000..817a78d2b0 --- /dev/null +++ b/examples/libraries/promiseExample.js @@ -0,0 +1,18 @@ +Script.include('promise.js'); +var Promise = loadPromise(); +var prom = new Promise(function(resolve, reject) { + print('making a promise') + // do a thing, possibly async, then… + var thing = true; + if (thing) { + resolve("Stuff worked!"); + } else { + print('ERROR') + reject(new Error("It broke")); + } +}); + +// Do something when async done +prom.then(function(result) { + print('result ' + result); +}); \ No newline at end of file From 7c433f47c01956a2c424e538d08478abe27a10e9 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 3 Nov 2015 17:39:21 -0800 Subject: [PATCH 7/8] Breaking up input devices and input plugins classes. --- interface/src/Application.cpp | 8 +- interface/src/Menu.cpp | 2 - .../openvr/OpenVrDisplayPlugin.cpp | 25 ++--- .../openvr/OpenVrDisplayPlugin.h | 2 + .../display-plugins/openvr/OpenVrHelpers.cpp | 73 +++++++++++++ .../display-plugins/openvr/OpenVrHelpers.h | 6 + .../src/input-plugins/KeyboardMouseDevice.cpp | 74 +++++++------ .../src/input-plugins/KeyboardMouseDevice.h | 49 ++++++--- .../src/input-plugins/SixenseManager.cpp | 101 +++++++++-------- .../src/input-plugins/SixenseManager.h | 91 +++++++++------- .../src/input-plugins/SpacemouseManager.cpp | 15 +-- .../src/input-plugins/SpacemouseManager.h | 3 - .../input-plugins/ViveControllerManager.cpp | 103 +++++++----------- .../src/input-plugins/ViveControllerManager.h | 54 +++++---- tests/controllers/src/main.cpp | 2 +- 15 files changed, 346 insertions(+), 262 deletions(-) create mode 100644 libraries/display-plugins/src/display-plugins/openvr/OpenVrHelpers.cpp diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8bff0f87dc..7a564bbbf0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -653,7 +653,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : userInputMapper->registerDevice(_applicationStateDevice); // Setup the keyboardMouseDevice and the user input mapper with the default bindings - userInputMapper->registerDevice(_keyboardMouseDevice); + userInputMapper->registerDevice(_keyboardMouseDevice->getInputDevice()); userInputMapper->loadDefaultMapping(userInputMapper->getStandardDeviceID()); @@ -729,8 +729,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : // Now that menu is initalized we can sync myAvatar with it's state. getMyAvatar()->updateMotionBehaviorFromMenu(); +// FIXME spacemouse code still needs cleanup +#if 0 // the 3Dconnexion device wants to be initiliazed after a window is displayed. SpacemouseManager::getInstance().init(); +#endif auto& packetReceiver = nodeList->getPacketReceiver(); packetReceiver.registerListener(PacketType::DomainConnectionDenied, this, "handleDomainConnectionDeniedPacket"); @@ -1850,9 +1853,12 @@ void Application::focusOutEvent(QFocusEvent* event) { } } +// FIXME spacemouse code still needs cleanup +#if 0 //SpacemouseDevice::getInstance().focusOutEvent(); //SpacemouseManager::getInstance().getDevice()->focusOutEvent(); SpacemouseManager::getInstance().ManagerFocusOutEvent(); +#endif // synthesize events for keys currently pressed, since we may not get their release events foreach (int key, _keysPressed) { diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 162b713948..92ff39a489 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -465,8 +465,6 @@ Menu::Menu() { avatar, SLOT(setEnableMeshVisible(bool))); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisableEyelidAdjustment, 0, false); - addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::Connexion, 0, false, &SpacemouseManager::getInstance(), SLOT(toggleSpacemouse(bool))); - addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ComfortMode, 0, true); MenuWrapper* handOptionsMenu = developerMenu->addMenu("Hands"); diff --git a/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp index 174bf1bf36..bb39c7bb7a 100644 --- a/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp @@ -37,8 +37,6 @@ const QString & OpenVrDisplayPlugin::getName() const { return NAME; } -vr::IVRSystem* _hmd{ nullptr }; -int hmdRefCount = 0; static vr::IVRCompositor* _compositor{ nullptr }; vr::TrackedDevicePose_t _trackedDevicePose[vr::k_unMaxTrackedDeviceCount]; mat4 _trackedDevicePoseMat4[vr::k_unMaxTrackedDeviceCount]; @@ -78,24 +76,17 @@ mat4 toGlm(const vr::HmdMatrix34_t& m) { } bool OpenVrDisplayPlugin::isSupported() const { - bool success = vr::VR_IsHmdPresent(); - if (success) { - vr::HmdError eError = vr::HmdError_None; - auto hmd = vr::VR_Init(&eError); - success = (hmd != nullptr); - vr::VR_Shutdown(); - } + auto hmd = acquireOpenVrSystem(); + bool success = nullptr != hmd; + releaseOpenVrSystem(); return success; } void OpenVrDisplayPlugin::activate() { _container->setIsOptionChecked(StandingHMDSensorMode, true); - hmdRefCount++; - vr::HmdError eError = vr::HmdError_None; if (!_hmd) { - _hmd = vr::VR_Init(&eError); - Q_ASSERT(eError == vr::HmdError_None); + _hmd = acquireOpenVrSystem(); } Q_ASSERT(_hmd); @@ -114,6 +105,7 @@ void OpenVrDisplayPlugin::activate() { }); + vr::HmdError eError = vr::HmdError_None; _compositor = (vr::IVRCompositor*)vr::VR_GetGenericInterface(vr::IVRCompositor_Version, &eError); Q_ASSERT(eError == vr::HmdError_None); Q_ASSERT(_compositor); @@ -133,11 +125,8 @@ void OpenVrDisplayPlugin::activate() { void OpenVrDisplayPlugin::deactivate() { _container->setIsOptionChecked(StandingHMDSensorMode, false); - - hmdRefCount--; - - if (hmdRefCount == 0 && _hmd) { - vr::VR_Shutdown(); + if (_hmd) { + releaseOpenVrSystem(); _hmd = nullptr; } _compositor = nullptr; diff --git a/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.h index 7849623552..15d37d9de8 100644 --- a/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.h @@ -10,6 +10,7 @@ #include #if defined(Q_OS_WIN) +#include #include "../WindowOpenGLDisplayPlugin.h" @@ -39,6 +40,7 @@ protected: virtual void finishFrame() override; private: + vr::IVRSystem* _hmd { nullptr }; static const QString NAME; }; diff --git a/libraries/display-plugins/src/display-plugins/openvr/OpenVrHelpers.cpp b/libraries/display-plugins/src/display-plugins/openvr/OpenVrHelpers.cpp new file mode 100644 index 0000000000..44d30962bd --- /dev/null +++ b/libraries/display-plugins/src/display-plugins/openvr/OpenVrHelpers.cpp @@ -0,0 +1,73 @@ +// +// Created by Bradley Austin Davis on 2015/11/01 +// 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 +// +#include "OpenVrHelpers.h" + +#if defined(Q_OS_WIN) + +#include + +#include +#include + +using Mutex = std::mutex; +using Lock = std::unique_lock; + +static int refCount { 0 }; +static Mutex mutex; +static vr::IVRSystem* activeHmd { nullptr }; +static bool hmdPresent = vr::VR_IsHmdPresent(); + +static const uint32_t RELEASE_OPENVR_HMD_DELAY_MS = 5000; + +vr::IVRSystem* acquireOpenVrSystem() { + if (hmdPresent) { + Lock lock(mutex); + if (!activeHmd) { + qDebug() << "openvr: No vr::IVRSystem instance active, building"; + vr::HmdError eError = vr::HmdError_None; + activeHmd = vr::VR_Init(&eError); + qDebug() << "openvr display: HMD is " << activeHmd << " error is " << eError; + } + if (activeHmd) { + qDebug() << "openvr: incrementing refcount"; + ++refCount; + } + } + return activeHmd; +} + +void releaseOpenVrSystem() { + if (activeHmd) { + Lock lock(mutex); + qDebug() << "openvr: decrementing refcount"; + --refCount; + if (0 == refCount) { + qDebug() << "openvr: zero refcount, deallocate VR system"; + // Avoid spamming the VR system with activate/deactivate calls at system startup by + // putting in a delay before we destory the shutdown the VR subsystem + + // FIXME releasing the VR system at all seems to trigger an exception deep inside the Oculus DLL. + // disabling for now. + //QTimer* releaseTimer = new QTimer(); + //releaseTimer->singleShot(RELEASE_OPENVR_HMD_DELAY_MS, [releaseTimer] { + // Lock lock(mutex); + // qDebug() << "Delayed openvr destroy activated"; + // if (0 == refCount && nullptr != activeHmd) { + // qDebug() << "Delayed openvr destroy: releasing resources"; + // activeHmd = nullptr; + // vr::VR_Shutdown(); + // } else { + // qDebug() << "Delayed openvr destroy: HMD still in use"; + // } + // releaseTimer->deleteLater(); + //}); + } + } +} + +#endif diff --git a/libraries/display-plugins/src/display-plugins/openvr/OpenVrHelpers.h b/libraries/display-plugins/src/display-plugins/openvr/OpenVrHelpers.h index 761bef8cfc..3e445d90ba 100644 --- a/libraries/display-plugins/src/display-plugins/openvr/OpenVrHelpers.h +++ b/libraries/display-plugins/src/display-plugins/openvr/OpenVrHelpers.h @@ -7,10 +7,16 @@ // #pragma once +#include + #if defined(Q_OS_WIN) #include #include #include #include + +vr::IVRSystem* acquireOpenVrSystem(); +void releaseOpenVrSystem(); + #endif diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp index 2157a3a010..e91ea90aaf 100755 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp @@ -19,8 +19,8 @@ const QString KeyboardMouseDevice::NAME = "Keyboard/Mouse"; -void KeyboardMouseDevice::update(float deltaTime, bool jointsCaptured) { - _axisStateMap.clear(); +void KeyboardMouseDevice::pluginUpdate(float deltaTime, bool jointsCaptured) { + _inputDevice->update(deltaTime, jointsCaptured); // For touch event, we need to check that the last event is not too long ago // Maybe it's a Qt issue, but the touch event sequence (begin, update, end) is not always called properly @@ -35,26 +35,30 @@ void KeyboardMouseDevice::update(float deltaTime, bool jointsCaptured) { } } -void KeyboardMouseDevice::focusOutEvent() { +void KeyboardMouseDevice::InputDevice::update(float deltaTime, bool jointsCaptured) { + _axisStateMap.clear(); +} + +void KeyboardMouseDevice::InputDevice::focusOutEvent() { _buttonPressedMap.clear(); -}; +} void KeyboardMouseDevice::keyPressEvent(QKeyEvent* event) { - auto input = makeInput((Qt::Key) event->key()); - auto result = _buttonPressedMap.insert(input.getChannel()); + auto input = _inputDevice->makeInput((Qt::Key) event->key()); + auto result = _inputDevice->_buttonPressedMap.insert(input.getChannel()); if (!result.second) { // key pressed again ? without catching the release event ? } } void KeyboardMouseDevice::keyReleaseEvent(QKeyEvent* event) { - auto input = makeInput((Qt::Key) event->key()); - _buttonPressedMap.erase(input.getChannel()); + auto input = _inputDevice->makeInput((Qt::Key) event->key()); + _inputDevice->_buttonPressedMap.erase(input.getChannel()); } void KeyboardMouseDevice::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { - auto input = makeInput((Qt::MouseButton) event->button()); - auto result = _buttonPressedMap.insert(input.getChannel()); + auto input = _inputDevice->makeInput((Qt::MouseButton) event->button()); + auto result = _inputDevice->_buttonPressedMap.insert(input.getChannel()); if (!result.second) { // key pressed again ? without catching the release event ? } @@ -65,32 +69,32 @@ void KeyboardMouseDevice::mousePressEvent(QMouseEvent* event, unsigned int devic } void KeyboardMouseDevice::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) { - auto input = makeInput((Qt::MouseButton) event->button()); - _buttonPressedMap.erase(input.getChannel()); + auto input = _inputDevice->makeInput((Qt::MouseButton) event->button()); + _inputDevice->_buttonPressedMap.erase(input.getChannel()); // if we pressed and released at the same location, then create a "_CLICKED" input for this button // we might want to add some small tolerance to this so if you do a small drag it still counts as // a clicked. if (_mousePressAt == event->pos()) { - _buttonPressedMap.insert(makeInput((Qt::MouseButton) event->button(), true).getChannel()); + _inputDevice->_buttonPressedMap.insert(_inputDevice->makeInput((Qt::MouseButton) event->button(), true).getChannel()); } } void KeyboardMouseDevice::eraseMouseClicked() { - _buttonPressedMap.erase(makeInput(Qt::LeftButton, true).getChannel()); - _buttonPressedMap.erase(makeInput(Qt::MiddleButton, true).getChannel()); - _buttonPressedMap.erase(makeInput(Qt::RightButton, true).getChannel()); + _inputDevice->_buttonPressedMap.erase(_inputDevice->makeInput(Qt::LeftButton, true).getChannel()); + _inputDevice->_buttonPressedMap.erase(_inputDevice->makeInput(Qt::MiddleButton, true).getChannel()); + _inputDevice->_buttonPressedMap.erase(_inputDevice->makeInput(Qt::RightButton, true).getChannel()); } void KeyboardMouseDevice::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) { QPoint currentPos = event->pos(); QPoint currentMove = currentPos - _lastCursor; - _axisStateMap[makeInput(MOUSE_AXIS_X_POS).getChannel()] = (currentMove.x() > 0 ? currentMove.x() : 0.0f); - _axisStateMap[makeInput(MOUSE_AXIS_X_NEG).getChannel()] = (currentMove.x() < 0 ? -currentMove.x() : 0.0f); + _inputDevice->_axisStateMap[MOUSE_AXIS_X_POS] = (currentMove.x() > 0 ? currentMove.x() : 0.0f); + _inputDevice->_axisStateMap[MOUSE_AXIS_X_NEG] = (currentMove.x() < 0 ? -currentMove.x() : 0.0f); // Y mouse is inverted positive is pointing up the screen - _axisStateMap[makeInput(MOUSE_AXIS_Y_POS).getChannel()] = (currentMove.y() < 0 ? -currentMove.y() : 0.0f); - _axisStateMap[makeInput(MOUSE_AXIS_Y_NEG).getChannel()] = (currentMove.y() > 0 ? currentMove.y() : 0.0f); + _inputDevice->_axisStateMap[MOUSE_AXIS_Y_POS] = (currentMove.y() < 0 ? -currentMove.y() : 0.0f); + _inputDevice->_axisStateMap[MOUSE_AXIS_Y_NEG] = (currentMove.y() > 0 ? currentMove.y() : 0.0f); _lastCursor = currentPos; @@ -100,10 +104,10 @@ void KeyboardMouseDevice::mouseMoveEvent(QMouseEvent* event, unsigned int device void KeyboardMouseDevice::wheelEvent(QWheelEvent* event) { auto currentMove = event->angleDelta() / 120.0f; - _axisStateMap[makeInput(MOUSE_AXIS_WHEEL_X_POS).getChannel()] = (currentMove.x() > 0 ? currentMove.x() : 0.0f); - _axisStateMap[makeInput(MOUSE_AXIS_WHEEL_X_NEG).getChannel()] = (currentMove.x() < 0 ? -currentMove.x() : 0.0f); - _axisStateMap[makeInput(MOUSE_AXIS_WHEEL_Y_POS).getChannel()] = (currentMove.y() > 0 ? currentMove.y() : 0.0f); - _axisStateMap[makeInput(MOUSE_AXIS_WHEEL_Y_NEG).getChannel()] = (currentMove.y() < 0 ? -currentMove.y() : 0.0f); + _inputDevice->_axisStateMap[_inputDevice->makeInput(MOUSE_AXIS_WHEEL_X_POS).getChannel()] = (currentMove.x() > 0 ? currentMove.x() : 0.0f); + _inputDevice->_axisStateMap[_inputDevice->makeInput(MOUSE_AXIS_WHEEL_X_NEG).getChannel()] = (currentMove.x() < 0 ? -currentMove.x() : 0.0f); + _inputDevice->_axisStateMap[_inputDevice->makeInput(MOUSE_AXIS_WHEEL_Y_POS).getChannel()] = (currentMove.y() > 0 ? currentMove.y() : 0.0f); + _inputDevice->_axisStateMap[_inputDevice->makeInput(MOUSE_AXIS_WHEEL_Y_NEG).getChannel()] = (currentMove.y() < 0 ? -currentMove.y() : 0.0f); } glm::vec2 evalAverageTouchPoints(const QList& points) { @@ -138,17 +142,17 @@ void KeyboardMouseDevice::touchUpdateEvent(const QTouchEvent* event) { } else { auto currentMove = currentPos - _lastTouch; - _axisStateMap[makeInput(TOUCH_AXIS_X_POS).getChannel()] = (currentMove.x > 0 ? currentMove.x : 0.0f); - _axisStateMap[makeInput(TOUCH_AXIS_X_NEG).getChannel()] = (currentMove.x < 0 ? -currentMove.x : 0.0f); + _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_X_POS).getChannel()] = (currentMove.x > 0 ? currentMove.x : 0.0f); + _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_X_NEG).getChannel()] = (currentMove.x < 0 ? -currentMove.x : 0.0f); // Y mouse is inverted positive is pointing up the screen - _axisStateMap[makeInput(TOUCH_AXIS_Y_POS).getChannel()] = (currentMove.y < 0 ? -currentMove.y : 0.0f); - _axisStateMap[makeInput(TOUCH_AXIS_Y_NEG).getChannel()] = (currentMove.y > 0 ? currentMove.y : 0.0f); + _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_Y_POS).getChannel()] = (currentMove.y < 0 ? -currentMove.y : 0.0f); + _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_Y_NEG).getChannel()] = (currentMove.y > 0 ? currentMove.y : 0.0f); } _lastTouch = currentPos; } -controller::Input KeyboardMouseDevice::makeInput(Qt::Key code) const { +controller::Input KeyboardMouseDevice::InputDevice::makeInput(Qt::Key code) const { auto shortCode = (uint16_t)(code & KEYBOARD_MASK); if (shortCode != code) { shortCode |= 0x0800; // add this bit instead of the way Qt::Key add a bit on the 3rd byte for some keys @@ -156,7 +160,7 @@ controller::Input KeyboardMouseDevice::makeInput(Qt::Key code) const { return controller::Input(_deviceID, shortCode, controller::ChannelType::BUTTON); } -controller::Input KeyboardMouseDevice::makeInput(Qt::MouseButton code, bool clicked) const { +controller::Input KeyboardMouseDevice::InputDevice::makeInput(Qt::MouseButton code, bool clicked) const { switch (code) { case Qt::LeftButton: return controller::Input(_deviceID, clicked ? MOUSE_BUTTON_LEFT_CLICKED : @@ -172,19 +176,19 @@ controller::Input KeyboardMouseDevice::makeInput(Qt::MouseButton code, bool clic }; } -controller::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::MouseAxisChannel axis) const { +controller::Input KeyboardMouseDevice::InputDevice::makeInput(KeyboardMouseDevice::MouseAxisChannel axis) const { return controller::Input(_deviceID, axis, controller::ChannelType::AXIS); } -controller::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::TouchAxisChannel axis) const { +controller::Input KeyboardMouseDevice::InputDevice::makeInput(KeyboardMouseDevice::TouchAxisChannel axis) const { return controller::Input(_deviceID, axis, controller::ChannelType::AXIS); } -controller::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::TouchButtonChannel button) const { +controller::Input KeyboardMouseDevice::InputDevice::makeInput(KeyboardMouseDevice::TouchButtonChannel button) const { return controller::Input(_deviceID, button, controller::ChannelType::BUTTON); } -controller::Input::NamedVector KeyboardMouseDevice::getAvailableInputs() const { +controller::Input::NamedVector KeyboardMouseDevice::InputDevice::getAvailableInputs() const { using namespace controller; static QVector availableInputs; static std::once_flag once; @@ -229,7 +233,7 @@ controller::Input::NamedVector KeyboardMouseDevice::getAvailableInputs() const { return availableInputs; } -QString KeyboardMouseDevice::getDefaultMappingConfig() const { +QString KeyboardMouseDevice::InputDevice::getDefaultMappingConfig() const { static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/keyboardMouse.json"; return MAPPING_JSON; } diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h index 5d86821db1..b0578e3a99 100644 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h @@ -24,7 +24,7 @@ class QKeyEvent; class QMouseEvent; class QWheelEvent; -class KeyboardMouseDevice : public InputPlugin, public controller::InputDevice { +class KeyboardMouseDevice : public InputPlugin { Q_OBJECT public: enum KeyboardChannel { @@ -64,22 +64,20 @@ public: TOUCH_BUTTON_PRESS = TOUCH_AXIS_Y_NEG + 1, }; - KeyboardMouseDevice() : InputDevice("Keyboard") {} + KeyboardMouseDevice() {} + + virtual ~KeyboardMouseDevice() {} + + controller::InputDevice::Pointer getInputDevice() { return _inputDevice; } // Plugin functions virtual bool isSupported() const override { return true; } virtual bool isJointController() const override { return false; } const QString& getName() const override { return NAME; } - virtual void pluginFocusOutEvent() override { focusOutEvent(); } - virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override { update(deltaTime, jointsCaptured); } + virtual void pluginFocusOutEvent() override { _inputDevice->focusOutEvent(); } + virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override; - // Device functions - virtual controller::Input::NamedVector getAvailableInputs() const override; - virtual QString getDefaultMappingConfig() const override; - virtual void update(float deltaTime, bool jointsCaptured) override; - virtual void focusOutEvent() override; - void keyPressEvent(QKeyEvent* event); void keyReleaseEvent(QKeyEvent* event); @@ -94,21 +92,36 @@ public: void wheelEvent(QWheelEvent* event); - // Let's make it easy for Qt because we assume we love Qt forever - controller::Input makeInput(Qt::Key code) const; - controller::Input makeInput(Qt::MouseButton code, bool clicked = false) const; - controller::Input makeInput(MouseAxisChannel axis) const; - controller::Input makeInput(TouchAxisChannel axis) const; - controller::Input makeInput(TouchButtonChannel button) const; - static const QString NAME; protected: + + class InputDevice : public controller::InputDevice { + public: + InputDevice() : controller::InputDevice("Keyboard") {} + private: + // Device functions + virtual controller::Input::NamedVector getAvailableInputs() const override; + virtual QString getDefaultMappingConfig() const override; + virtual void update(float deltaTime, bool jointsCaptured) override; + virtual void focusOutEvent() override; + + // Let's make it easy for Qt because we assume we love Qt forever + controller::Input makeInput(Qt::Key code) const; + controller::Input makeInput(Qt::MouseButton code, bool clicked = false) const; + controller::Input makeInput(MouseAxisChannel axis) const; + controller::Input makeInput(TouchAxisChannel axis) const; + controller::Input makeInput(TouchButtonChannel button) const; + + friend class KeyboardMouseDevice; + }; + QPoint _lastCursor; QPoint _mousePressAt; glm::vec2 _lastTouch; + std::shared_ptr _inputDevice { std::make_shared() }; + bool _isTouching = false; - std::chrono::high_resolution_clock _clock; std::chrono::high_resolution_clock::time_point _lastTouchTime; }; diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp index 024eb86182..33b4332430 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp @@ -30,6 +30,11 @@ #ifdef HAVE_SIXENSE #include "sixense.h" + +#ifdef __APPLE__ +static QLibrary* _sixenseLibrary { nullptr }; +#endif + #endif // TODO: This should not be here @@ -39,13 +44,10 @@ Q_LOGGING_CATEGORY(inputplugins, "hifi.inputplugins") #ifdef HAVE_SIXENSE -const int CALIBRATION_STATE_IDLE = 0; -const int CALIBRATION_STATE_IN_PROGRESS = 1; -const int CALIBRATION_STATE_COMPLETE = 2; -const glm::vec3 DEFAULT_AVATAR_POSITION(-0.25f, -0.35f, -0.3f); // in hydra frame - -const float CONTROLLER_THRESHOLD = 0.35f; +const glm::vec3 SixenseManager::DEFAULT_AVATAR_POSITION { -0.25f, -0.35f, -0.3f }; // in hydra frame +const float SixenseManager::CONTROLLER_THRESHOLD { 0.35f }; +const float SixenseManager::DEFAULT_REACH_LENGTH { 1.5f }; #endif @@ -66,14 +68,6 @@ const QString MENU_PATH = MENU_PARENT + ">" + MENU_NAME; const QString TOGGLE_SMOOTH = "Smooth Sixense Movement"; const float DEFAULT_REACH_LENGTH = 1.5f; -static std::shared_ptr instance; -SixenseManager::SixenseManager() : - InputDevice("Hydra"), - _reachLength(DEFAULT_REACH_LENGTH) -{ - instance = std::shared_ptr(this); -} - bool SixenseManager::isSupported() const { #ifdef HAVE_SIXENSE @@ -91,8 +85,6 @@ bool SixenseManager::isSupported() const { void SixenseManager::activate() { InputPlugin::activate(); #ifdef HAVE_SIXENSE - _calibrationState = CALIBRATION_STATE_IDLE; - _avatarPosition = DEFAULT_AVATAR_POSITION; _container->addMenu(MENU_PATH); _container->addMenuItem(MENU_PATH, TOGGLE_SMOOTH, @@ -100,7 +92,7 @@ void SixenseManager::activate() { true, true); auto userInputMapper = DependencyManager::get(); - userInputMapper->registerDevice(instance); + userInputMapper->registerDevice(_inputDevice); #ifdef __APPLE__ @@ -139,12 +131,12 @@ void SixenseManager::deactivate() { _container->removeMenuItem(MENU_NAME, TOGGLE_SMOOTH); _container->removeMenu(MENU_PATH); - _poseStateMap.clear(); - _collectedSamples.clear(); + _inputDevice->_poseStateMap.clear(); + _inputDevice->_collectedSamples.clear(); - if (_deviceID != controller::Input::INVALID_DEVICE) { + if (_inputDevice->_deviceID != controller::Input::INVALID_DEVICE) { auto userInputMapper = DependencyManager::get(); - userInputMapper->removeDevice(_deviceID); + userInputMapper->removeDevice(_inputDevice->_deviceID); } #ifdef __APPLE__ @@ -170,7 +162,15 @@ void SixenseManager::setSixenseFilter(bool filter) { #endif } -void SixenseManager::update(float deltaTime, bool jointsCaptured) { +void SixenseManager::pluginUpdate(float deltaTime, bool jointsCaptured) { + _inputDevice->update(deltaTime, jointsCaptured); + if (_inputDevice->_calibrationState == CALIBRATION_STATE_COMPLETE) { + _container->requestReset(); + _inputDevice->_calibrationState = CALIBRATION_STATE_IDLE; + } +} + +void SixenseManager::InputDevice::update(float deltaTime, bool jointsCaptured) { // FIXME - Some of the code in update() will crash if you haven't actually activated the // plugin. But we want register with the UserInputMapper if we don't call this. // We need to clean this up. @@ -297,7 +297,7 @@ const float MINIMUM_ARM_REACH = 0.3f; // meters const float MAXIMUM_NOISE_LEVEL = 0.05f; // meters const quint64 LOCK_DURATION = USECS_PER_SECOND / 4; // time for lock to be acquired -void SixenseManager::updateCalibration(void* controllersX) { +void SixenseManager::InputDevice::updateCalibration(void* controllersX) { auto controllers = reinterpret_cast(controllersX); const sixenseControllerData* dataLeft = controllers; const sixenseControllerData* dataRight = controllers + 1; @@ -309,26 +309,25 @@ void SixenseManager::updateCalibration(void* controllersX) { } switch (_calibrationState) { case CALIBRATION_STATE_COMPLETE: - { - // compute calibration results - _avatarPosition = - 0.5f * (_reachLeft + _reachRight); // neck is midway between right and left hands - glm::vec3 xAxis = glm::normalize(_reachRight - _reachLeft); - glm::vec3 zAxis = glm::normalize(glm::cross(xAxis, Vectors::UNIT_Y)); - xAxis = glm::normalize(glm::cross(Vectors::UNIT_Y, zAxis)); - _reachLength = glm::dot(xAxis, _reachRight - _reachLeft); - _avatarRotation = glm::inverse(glm::quat_cast(glm::mat3(xAxis, Vectors::UNIT_Y, zAxis))); - const float Y_OFFSET_CALIBRATED_HANDS_TO_AVATAR = -0.3f; - _avatarPosition.y += Y_OFFSET_CALIBRATED_HANDS_TO_AVATAR; - _container->requestReset(); - qCDebug(inputplugins, "succeess: sixense calibration"); - } - break; + { + // compute calibration results + _avatarPosition = - 0.5f * (_reachLeft + _reachRight); // neck is midway between right and left hands + glm::vec3 xAxis = glm::normalize(_reachRight - _reachLeft); + glm::vec3 zAxis = glm::normalize(glm::cross(xAxis, Vectors::UNIT_Y)); + xAxis = glm::normalize(glm::cross(Vectors::UNIT_Y, zAxis)); + _reachLength = glm::dot(xAxis, _reachRight - _reachLeft); + _avatarRotation = glm::inverse(glm::quat_cast(glm::mat3(xAxis, Vectors::UNIT_Y, zAxis))); + const float Y_OFFSET_CALIBRATED_HANDS_TO_AVATAR = -0.3f; + _avatarPosition.y += Y_OFFSET_CALIBRATED_HANDS_TO_AVATAR; + qCDebug(inputplugins, "succeess: sixense calibration"); + } + break; + default: + _calibrationState = CALIBRATION_STATE_IDLE; qCDebug(inputplugins, "failed: sixense calibration"); break; } - - _calibrationState = CALIBRATION_STATE_IDLE; return; } @@ -382,15 +381,15 @@ void SixenseManager::updateCalibration(void* controllersX) { #endif // HAVE_SIXENSE -void SixenseManager::focusOutEvent() { +void SixenseManager::InputDevice::focusOutEvent() { _axisStateMap.clear(); _buttonPressedMap.clear(); }; -void SixenseManager::handleAxisEvent(float stickX, float stickY, float trigger, bool left) { +void SixenseManager::InputDevice::handleAxisEvent(float stickX, float stickY, float trigger, bool left) { } -void SixenseManager::handleButtonEvent(unsigned int buttons, bool left) { +void SixenseManager::InputDevice::handleButtonEvent(unsigned int buttons, bool left) { using namespace controller; if (buttons & BUTTON_0) { _buttonPressedMap.insert(left ? BACK : START); @@ -415,7 +414,7 @@ void SixenseManager::handleButtonEvent(unsigned int buttons, bool left) { } } -void SixenseManager::handlePoseEvent(float deltaTime, glm::vec3 position, glm::quat rotation, bool left) { +void SixenseManager::InputDevice::handlePoseEvent(float deltaTime, glm::vec3 position, glm::quat rotation, bool left) { #ifdef HAVE_SIXENSE auto hand = left ? controller::StandardPoseChannel::LEFT_HAND : controller::StandardPoseChannel::RIGHT_HAND; @@ -521,7 +520,7 @@ static const auto R4 = controller::Y; using namespace controller; -controller::Input::NamedVector SixenseManager::getAvailableInputs() const { +controller::Input::NamedVector SixenseManager::InputDevice::getAvailableInputs() const { using namespace controller; static const Input::NamedVector availableInputs { makePair(L0, "L0"), @@ -551,7 +550,7 @@ controller::Input::NamedVector SixenseManager::getAvailableInputs() const { }; -QString SixenseManager::getDefaultMappingConfig() const { +QString SixenseManager::InputDevice::getDefaultMappingConfig() const { static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/hydra.json"; return MAPPING_JSON; } @@ -562,9 +561,9 @@ void SixenseManager::saveSettings() const { QString idString = getID(); settings.beginGroup(idString); { - settings.setVec3Value(QString("avatarPosition"), _avatarPosition); - settings.setQuatValue(QString("avatarRotation"), _avatarRotation); - settings.setValue(QString("reachLength"), QVariant(_reachLength)); + settings.setVec3Value(QString("avatarPosition"), _inputDevice->_avatarPosition); + settings.setQuatValue(QString("avatarRotation"), _inputDevice->_avatarRotation); + settings.setValue(QString("reachLength"), QVariant(_inputDevice->_reachLength)); } settings.endGroup(); } @@ -574,9 +573,9 @@ void SixenseManager::loadSettings() { QString idString = getID(); settings.beginGroup(idString); { - settings.getVec3ValueIfValid(QString("avatarPosition"), _avatarPosition); - settings.getQuatValueIfValid(QString("avatarRotation"), _avatarRotation); - settings.getFloatValueIfValid(QString("reachLength"), _reachLength); + settings.getVec3ValueIfValid(QString("avatarPosition"), _inputDevice->_avatarPosition); + settings.getQuatValueIfValid(QString("avatarRotation"), _inputDevice->_avatarRotation); + settings.getFloatValueIfValid(QString("reachLength"), _inputDevice->_reachLength); } settings.endGroup(); } diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.h b/libraries/input-plugins/src/input-plugins/SixenseManager.h index 5b5cb7ccfa..7a686dc423 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.h +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.h @@ -44,10 +44,11 @@ const unsigned int BUTTON_TRIGGER = 1U << 8; const bool DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS = false; // Handles interaction with the Sixense SDK (e.g., Razer Hydra). -class SixenseManager : public InputPlugin, public controller::InputDevice { +class SixenseManager : public InputPlugin { Q_OBJECT public: - SixenseManager(); + SixenseManager() {} + virtual ~SixenseManager() {} // Plugin functions virtual bool isSupported() const override; @@ -58,15 +59,8 @@ public: virtual void activate() override; virtual void deactivate() override; - virtual void pluginFocusOutEvent() override { focusOutEvent(); } - virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override { update(deltaTime, jointsCaptured); } - - // Device functions - virtual controller::Input::NamedVector getAvailableInputs() const override; - virtual QString getDefaultMappingConfig() const override; - - virtual void update(float deltaTime, bool jointsCaptured) override; - virtual void focusOutEvent() override; + virtual void pluginFocusOutEvent() override { _inputDevice->focusOutEvent(); } + virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override; virtual void saveSettings() const override; virtual void loadSettings() override; @@ -75,38 +69,57 @@ public slots: void setSixenseFilter(bool filter); private: - void handleButtonEvent(unsigned int buttons, bool left); - void handleAxisEvent(float x, float y, float trigger, bool left); - void handlePoseEvent(float deltaTime, glm::vec3 position, glm::quat rotation, bool left); - - void updateCalibration(void* controllers); - - int _calibrationState; - - // these are calibration results - glm::vec3 _avatarPosition; // in hydra-frame - glm::quat _avatarRotation; // in hydra-frame - float _reachLength; - - // these are measured values used to compute the calibration results - quint64 _lockExpiry; - glm::vec3 _averageLeft; - glm::vec3 _averageRight; - glm::vec3 _reachLeft; - glm::vec3 _reachRight; - float _lastDistance; - bool _useSixenseFilter = true; - - static const int MAX_NUM_AVERAGING_SAMPLES = 50; // At ~100 updates per seconds this means averaging over ~.5s + static const int CALIBRATION_STATE_IDLE = 0; + static const int CALIBRATION_STATE_IN_PROGRESS = 1; + static const int CALIBRATION_STATE_COMPLETE = 2; + static const glm::vec3 DEFAULT_AVATAR_POSITION; + static const float CONTROLLER_THRESHOLD; + static const float DEFAULT_REACH_LENGTH; + + using Samples = std::pair< MovingAverage< glm::vec3, MAX_NUM_AVERAGING_SAMPLES>, MovingAverage< glm::vec4, MAX_NUM_AVERAGING_SAMPLES> >; using MovingAverageMap = std::map< int, Samples >; - MovingAverageMap _collectedSamples; - -#ifdef __APPLE__ - QLibrary* _sixenseLibrary { nullptr }; -#endif + + class InputDevice : public controller::InputDevice { + public: + InputDevice() : controller::InputDevice("Hydra") {} + private: + // Device functions + virtual controller::Input::NamedVector getAvailableInputs() const override; + virtual QString getDefaultMappingConfig() const override; + virtual void update(float deltaTime, bool jointsCaptured) override; + virtual void focusOutEvent() override; + + void handleButtonEvent(unsigned int buttons, bool left); + void handleAxisEvent(float x, float y, float trigger, bool left); + void handlePoseEvent(float deltaTime, glm::vec3 position, glm::quat rotation, bool left); + void updateCalibration(void* controllers); + + friend class SixenseManager; + + MovingAverageMap _collectedSamples; + + int _calibrationState { CALIBRATION_STATE_IDLE }; + // these are calibration results + glm::vec3 _avatarPosition { DEFAULT_AVATAR_POSITION }; // in hydra-frame + glm::quat _avatarRotation; // in hydra-frame + float _reachLength { DEFAULT_REACH_LENGTH }; + float _lastDistance; + // these are measured values used to compute the calibration results + quint64 _lockExpiry; + glm::vec3 _averageLeft; + glm::vec3 _averageRight; + glm::vec3 _reachLeft; + glm::vec3 _reachRight; + }; + + + + bool _useSixenseFilter = true; + std::shared_ptr _inputDevice { std::make_shared() }; + static const QString NAME; static const QString HYDRA_ID_STRING; }; diff --git a/libraries/input-plugins/src/input-plugins/SpacemouseManager.cpp b/libraries/input-plugins/src/input-plugins/SpacemouseManager.cpp index 43e6ee48a8..fe90470cb4 100644 --- a/libraries/input-plugins/src/input-plugins/SpacemouseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SpacemouseManager.cpp @@ -21,11 +21,10 @@ const float MAX_AXIS = 75.0f; // max forward = 2x speed -static std::shared_ptr instance; -SpacemouseDevice::SpacemouseDevice() : -InputDevice("Spacemouse") +static std::shared_ptr instance = std::make_shared(); + +SpacemouseDevice::SpacemouseDevice() : InputDevice("Spacemouse") { - instance = std::shared_ptr(this); } void SpacemouseDevice::focusOutEvent() { @@ -118,14 +117,6 @@ void SpacemouseDevice::update(float deltaTime, bool jointsCaptured) { // for osx the api will call DeviceAddedHandler or DeviceRemoveHandler when a 3Dconnexion device is attached or detached } -SpacemouseManager& SpacemouseManager::getInstance() { - static SpacemouseManager sharedInstance; - if (instance == nullptr) { - new SpacemouseDevice(); - } - return sharedInstance; -} - void SpacemouseManager::ManagerFocusOutEvent() { instance->focusOutEvent(); } diff --git a/libraries/input-plugins/src/input-plugins/SpacemouseManager.h b/libraries/input-plugins/src/input-plugins/SpacemouseManager.h index 08ac954c94..6253fa7f9d 100644 --- a/libraries/input-plugins/src/input-plugins/SpacemouseManager.h +++ b/libraries/input-plugins/src/input-plugins/SpacemouseManager.h @@ -23,7 +23,6 @@ class SpacemouseManager : public QObject { Q_OBJECT public: - static SpacemouseManager& getInstance(); void ManagerFocusOutEvent(); void init(); void destroy() {}; @@ -92,7 +91,6 @@ class SpacemouseManager : public QObject, public QAbstractNativeEventFilter { public: SpacemouseManager() {}; - static SpacemouseManager& getInstance(); void init(); void destroy(); bool Is3dmouseAttached(); @@ -169,7 +167,6 @@ private: class SpacemouseManager : public QObject { Q_OBJECT public: - static SpacemouseManager& getInstance(); void init(); void destroy(); bool Is3dmouseAttached(); diff --git a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp index 69b2b5b2c6..ec0c35cc96 100644 --- a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp +++ b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp @@ -27,12 +27,13 @@ #include #ifdef Q_OS_WIN -extern vr::IVRSystem* _hmd; -extern int hmdRefCount; extern vr::TrackedDevicePose_t _trackedDevicePose[vr::k_unMaxTrackedDeviceCount]; extern mat4 _trackedDevicePoseMat4[vr::k_unMaxTrackedDeviceCount]; #endif +vr::IVRSystem* acquireOpenVrSystem(); +void releaseOpenVrSystem(); + const float CONTROLLER_LENGTH_OFFSET = 0.0762f; // three inches const QString CONTROLLER_MODEL_STRING = "vr_controller_05_wireless_b"; @@ -44,28 +45,11 @@ const QString MENU_NAME = "Vive Controllers"; const QString MENU_PATH = MENU_PARENT + ">" + MENU_NAME; const QString RENDER_CONTROLLERS = "Render Hand Controllers"; -static std::shared_ptr instance; - -ViveControllerManager::ViveControllerManager() : - InputDevice("Vive"), - _trackedControllers(0), - _modelLoaded(false), - _leftHandRenderID(0), - _rightHandRenderID(0), - _renderControllers(false) -{ - instance = std::shared_ptr(this); -} - bool ViveControllerManager::isSupported() const { #ifdef Q_OS_WIN - bool success = vr::VR_IsHmdPresent(); - if (success) { - vr::HmdError eError = vr::HmdError_None; - auto hmd = vr::VR_Init(&eError); - success = (hmd != nullptr); - vr::VR_Shutdown(); - } + auto hmd = acquireOpenVrSystem(); + bool success = hmd != nullptr; + releaseOpenVrSystem(); return success; #else return false; @@ -80,11 +64,8 @@ void ViveControllerManager::activate() { [this] (bool clicked) { this->setRenderControllers(clicked); }, true, true); - hmdRefCount++; if (!_hmd) { - vr::HmdError eError = vr::HmdError_None; - _hmd = vr::VR_Init(&eError); - Q_ASSERT(eError == vr::HmdError_None); + _hmd = acquireOpenVrSystem(); } Q_ASSERT(_hmd); @@ -138,7 +119,7 @@ void ViveControllerManager::activate() { // unregister with UserInputMapper auto userInputMapper = DependencyManager::get(); - userInputMapper->registerDevice(instance); + userInputMapper->registerDevice(_inputDevice); _registeredWithInputMapper = true; } @@ -149,18 +130,17 @@ void ViveControllerManager::deactivate() { _container->removeMenuItem(MENU_NAME, RENDER_CONTROLLERS); _container->removeMenu(MENU_PATH); - hmdRefCount--; - - if (hmdRefCount == 0 && _hmd) { - vr::VR_Shutdown(); + if (_hmd) { + releaseOpenVrSystem(); _hmd = nullptr; } - _poseStateMap.clear(); + + _inputDevice->_poseStateMap.clear(); #endif // unregister with UserInputMapper auto userInputMapper = DependencyManager::get(); - userInputMapper->removeDevice(_deviceID); + userInputMapper->removeDevice(_inputDevice->_deviceID); _registeredWithInputMapper = false; } @@ -177,8 +157,8 @@ void ViveControllerManager::updateRendering(RenderArgs* args, render::ScenePoint //pendingChanges.updateItem(_leftHandRenderID, ); - controller::Pose leftHand = _poseStateMap[controller::StandardPoseChannel::LEFT_HAND]; - controller::Pose rightHand = _poseStateMap[controller::StandardPoseChannel::RIGHT_HAND]; + controller::Pose leftHand = _inputDevice->_poseStateMap[controller::StandardPoseChannel::LEFT_HAND]; + controller::Pose rightHand = _inputDevice->_poseStateMap[controller::StandardPoseChannel::RIGHT_HAND]; gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { auto geometryCache = DependencyManager::get(); @@ -223,15 +203,28 @@ void ViveControllerManager::renderHand(const controller::Pose& pose, gpu::Batch& batch.drawIndexed(gpu::TRIANGLES, mesh->getNumIndices(), 0); } -void ViveControllerManager::update(float deltaTime, bool jointsCaptured) { + +void ViveControllerManager::pluginUpdate(float deltaTime, bool jointsCaptured) { + _inputDevice->update(deltaTime, jointsCaptured); + auto userInputMapper = DependencyManager::get(); + + if (_inputDevice->_trackedControllers == 0 && _registeredWithInputMapper) { + userInputMapper->removeDevice(_inputDevice->_deviceID); + _registeredWithInputMapper = false; + _inputDevice->_poseStateMap.clear(); + } + + if (!_registeredWithInputMapper && _inputDevice->_trackedControllers > 0) { + userInputMapper->registerDevice(_inputDevice); + _registeredWithInputMapper = true; + UserActivityLogger::getInstance().connectedDevice("spatial_controller", "steamVR"); + } +} + +void ViveControllerManager::InputDevice::update(float deltaTime, bool jointsCaptured) { #ifdef Q_OS_WIN _poseStateMap.clear(); - // TODO: This shouldn't be necessary - if (!_hmd) { - return; - } - _buttonPressedMap.clear(); PerformanceTimer perfTimer("ViveControllerManager::update"); @@ -279,33 +272,17 @@ void ViveControllerManager::update(float deltaTime, bool jointsCaptured) { } } - auto userInputMapper = DependencyManager::get(); - - if (numTrackedControllers == 0) { - if (_registeredWithInputMapper) { - userInputMapper->removeDevice(_deviceID); - _registeredWithInputMapper = false; - _poseStateMap.clear(); - } - } - - if (_trackedControllers == 0 && numTrackedControllers > 0) { - userInputMapper->registerDevice(instance); - _registeredWithInputMapper = true; - UserActivityLogger::getInstance().connectedDevice("spatial_controller", "steamVR"); - } - _trackedControllers = numTrackedControllers; #endif } -void ViveControllerManager::focusOutEvent() { +void ViveControllerManager::InputDevice::focusOutEvent() { _axisStateMap.clear(); _buttonPressedMap.clear(); }; // These functions do translation from the Steam IDs to the standard controller IDs -void ViveControllerManager::handleAxisEvent(uint32_t axis, float x, float y, bool left) { +void ViveControllerManager::InputDevice::handleAxisEvent(uint32_t axis, float x, float y, bool left) { #ifdef Q_OS_WIN //FIX ME? It enters here every frame: probably we want to enter only if an event occurs axis += vr::k_EButton_Axis0; @@ -320,7 +297,7 @@ void ViveControllerManager::handleAxisEvent(uint32_t axis, float x, float y, boo } // These functions do translation from the Steam IDs to the standard controller IDs -void ViveControllerManager::handleButtonEvent(uint32_t button, bool pressed, bool left) { +void ViveControllerManager::InputDevice::handleButtonEvent(uint32_t button, bool pressed, bool left) { #ifdef Q_OS_WIN if (!pressed) { return; @@ -342,7 +319,7 @@ void ViveControllerManager::handleButtonEvent(uint32_t button, bool pressed, boo #endif } -void ViveControllerManager::handlePoseEvent(const mat4& mat, bool left) { +void ViveControllerManager::InputDevice::handlePoseEvent(const mat4& mat, bool left) { glm::vec3 position = extractTranslation(mat); glm::quat rotation = glm::quat_cast(mat); @@ -409,7 +386,7 @@ void ViveControllerManager::handlePoseEvent(const mat4& mat, bool left) { _poseStateMap[left ? controller::LEFT_HAND : controller::RIGHT_HAND] = controller::Pose(position, rotation); } -controller::Input::NamedVector ViveControllerManager::getAvailableInputs() const { +controller::Input::NamedVector ViveControllerManager::InputDevice::getAvailableInputs() const { using namespace controller; QVector availableInputs{ // Trackpad analogs @@ -450,7 +427,7 @@ controller::Input::NamedVector ViveControllerManager::getAvailableInputs() const return availableInputs; } -QString ViveControllerManager::getDefaultMappingConfig() const { +QString ViveControllerManager::InputDevice::getDefaultMappingConfig() const { static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/vive.json"; return MAPPING_JSON; } diff --git a/libraries/input-plugins/src/input-plugins/ViveControllerManager.h b/libraries/input-plugins/src/input-plugins/ViveControllerManager.h index 938f2f9ba9..a925733327 100644 --- a/libraries/input-plugins/src/input-plugins/ViveControllerManager.h +++ b/libraries/input-plugins/src/input-plugins/ViveControllerManager.h @@ -24,10 +24,15 @@ #include #include -class ViveControllerManager : public InputPlugin, public controller::InputDevice { +namespace vr { + class IVRSystem; +} + +class ViveControllerManager : public InputPlugin { Q_OBJECT public: - ViveControllerManager(); + ViveControllerManager() {} + virtual ~ViveControllerManager() {} // Plugin functions virtual bool isSupported() const override; @@ -37,40 +42,51 @@ public: virtual void activate() override; virtual void deactivate() override; - virtual void pluginFocusOutEvent() override { focusOutEvent(); } - virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override { update(deltaTime, jointsCaptured); } - - // Device functions - virtual controller::Input::NamedVector getAvailableInputs() const override; - virtual QString getDefaultMappingConfig() const override; - virtual void update(float deltaTime, bool jointsCaptured) override; - virtual void focusOutEvent() override; + virtual void pluginFocusOutEvent() override { _inputDevice->focusOutEvent(); } + virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override; void updateRendering(RenderArgs* args, render::ScenePointer scene, render::PendingChanges pendingChanges); void setRenderControllers(bool renderControllers) { _renderControllers = renderControllers; } private: + class InputDevice : public controller::InputDevice { + public: + InputDevice(vr::IVRSystem*& hmd) : controller::InputDevice("Vive"), _hmd(hmd) {} + private: + // Device functions + virtual controller::Input::NamedVector getAvailableInputs() const override; + virtual QString getDefaultMappingConfig() const override; + virtual void update(float deltaTime, 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 handlePoseEvent(const mat4& mat, bool left); + + int _trackedControllers { 0 }; + vr::IVRSystem*& _hmd; + friend class ViveControllerManager; + }; + void renderHand(const controller::Pose& pose, gpu::Batch& batch, int sign); - void handleButtonEvent(uint32_t button, bool pressed, bool left); - void handleAxisEvent(uint32_t axis, float x, float y, bool left); - void handlePoseEvent(const mat4& mat, bool left); - int _trackedControllers; - bool _modelLoaded; + bool _registeredWithInputMapper { false }; + bool _modelLoaded { false }; model::Geometry _modelGeometry; gpu::TexturePointer _texture; - int _leftHandRenderID; - int _rightHandRenderID; + int _leftHandRenderID { 0 }; + int _rightHandRenderID { 0 }; - bool _renderControllers; + bool _renderControllers { false }; + vr::IVRSystem* _hmd { nullptr }; + std::shared_ptr _inputDevice { std::make_shared(_hmd) }; static const QString NAME; - bool _registeredWithInputMapper { false }; }; diff --git a/tests/controllers/src/main.cpp b/tests/controllers/src/main.cpp index 6e2732f1d8..6cc2cfc6eb 100644 --- a/tests/controllers/src/main.cpp +++ b/tests/controllers/src/main.cpp @@ -132,7 +132,7 @@ int main(int argc, char** argv) { inputPlugin->activate(); auto userInputMapper = DependencyManager::get(); if (name == KeyboardMouseDevice::NAME) { - userInputMapper->registerDevice(std::dynamic_pointer_cast(inputPlugin)); + userInputMapper->registerDevice(dynamic_cast(inputPlugin.get())->getInputDevice()); } inputPlugin->pluginUpdate(0, false); } From bf70ae4724fd16cd04d220d0a76a5481bc800929 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 6 Nov 2015 15:59:00 -0800 Subject: [PATCH 8/8] PR feedback --- .../src/display-plugins/Logging.cpp | 11 +++++++++++ .../src/display-plugins/Logging.h | 16 ++++++++++++++++ .../src/display-plugins/openvr/OpenVrHelpers.cpp | 8 +++++--- .../src/input-plugins/KeyboardMouseDevice.h | 10 ++++------ .../src/input-plugins/SixenseManager.h | 4 +--- .../src/input-plugins/ViveControllerManager.h | 2 -- tests/controllers/src/main.cpp | 2 +- 7 files changed, 38 insertions(+), 15 deletions(-) create mode 100644 libraries/display-plugins/src/display-plugins/Logging.cpp create mode 100644 libraries/display-plugins/src/display-plugins/Logging.h diff --git a/libraries/display-plugins/src/display-plugins/Logging.cpp b/libraries/display-plugins/src/display-plugins/Logging.cpp new file mode 100644 index 0000000000..00cd4cf0f9 --- /dev/null +++ b/libraries/display-plugins/src/display-plugins/Logging.cpp @@ -0,0 +1,11 @@ +// +// Created by Bradley Austin Davis 2015/10/11 +// 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 +// + +#include "Logging.h" + +Q_LOGGING_CATEGORY(displayPlugins, "hifi.plugins.display") diff --git a/libraries/display-plugins/src/display-plugins/Logging.h b/libraries/display-plugins/src/display-plugins/Logging.h new file mode 100644 index 0000000000..79b20b5dcc --- /dev/null +++ b/libraries/display-plugins/src/display-plugins/Logging.h @@ -0,0 +1,16 @@ +// +// Created by Bradley Austin Davis 2015/10/11 +// 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 +// + +#ifndef hifi_DisplayPlugins_Logging_h +#define hifi_DisplayPlugins_Logging_h + +#include + +Q_DECLARE_LOGGING_CATEGORY(displayPlugins) + +#endif diff --git a/libraries/display-plugins/src/display-plugins/openvr/OpenVrHelpers.cpp b/libraries/display-plugins/src/display-plugins/openvr/OpenVrHelpers.cpp index 44d30962bd..f8e810beaf 100644 --- a/libraries/display-plugins/src/display-plugins/openvr/OpenVrHelpers.cpp +++ b/libraries/display-plugins/src/display-plugins/openvr/OpenVrHelpers.cpp @@ -14,6 +14,8 @@ #include #include +#include "../Logging.h" + using Mutex = std::mutex; using Lock = std::unique_lock; @@ -28,13 +30,13 @@ vr::IVRSystem* acquireOpenVrSystem() { if (hmdPresent) { Lock lock(mutex); if (!activeHmd) { - qDebug() << "openvr: No vr::IVRSystem instance active, building"; + qCDebug(displayPlugins) << "openvr: No vr::IVRSystem instance active, building"; vr::HmdError eError = vr::HmdError_None; activeHmd = vr::VR_Init(&eError); - qDebug() << "openvr display: HMD is " << activeHmd << " error is " << eError; + qCDebug(displayPlugins) << "openvr display: HMD is " << activeHmd << " error is " << eError; } if (activeHmd) { - qDebug() << "openvr: incrementing refcount"; + qCDebug(displayPlugins) << "openvr: incrementing refcount"; ++refCount; } } diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h index b0578e3a99..4abdc44478 100644 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h @@ -64,12 +64,6 @@ public: TOUCH_BUTTON_PRESS = TOUCH_AXIS_Y_NEG + 1, }; - KeyboardMouseDevice() {} - - virtual ~KeyboardMouseDevice() {} - - controller::InputDevice::Pointer getInputDevice() { return _inputDevice; } - // Plugin functions virtual bool isSupported() const override { return true; } virtual bool isJointController() const override { return false; } @@ -116,6 +110,10 @@ protected: friend class KeyboardMouseDevice; }; +public: + const std::shared_ptr& getInputDevice() const { return _inputDevice; } + +protected: QPoint _lastCursor; QPoint _mousePressAt; glm::vec2 _lastTouch; diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.h b/libraries/input-plugins/src/input-plugins/SixenseManager.h index 7a686dc423..062a27390c 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.h +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.h @@ -47,9 +47,7 @@ const bool DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS = false; class SixenseManager : public InputPlugin { Q_OBJECT public: - SixenseManager() {} - virtual ~SixenseManager() {} - + // Plugin functions virtual bool isSupported() const override; virtual bool isJointController() const override { return true; } diff --git a/libraries/input-plugins/src/input-plugins/ViveControllerManager.h b/libraries/input-plugins/src/input-plugins/ViveControllerManager.h index a925733327..02bdecb10a 100644 --- a/libraries/input-plugins/src/input-plugins/ViveControllerManager.h +++ b/libraries/input-plugins/src/input-plugins/ViveControllerManager.h @@ -31,8 +31,6 @@ namespace vr { class ViveControllerManager : public InputPlugin { Q_OBJECT public: - ViveControllerManager() {} - virtual ~ViveControllerManager() {} // Plugin functions virtual bool isSupported() const override; diff --git a/tests/controllers/src/main.cpp b/tests/controllers/src/main.cpp index 6cc2cfc6eb..664a894e44 100644 --- a/tests/controllers/src/main.cpp +++ b/tests/controllers/src/main.cpp @@ -132,7 +132,7 @@ int main(int argc, char** argv) { inputPlugin->activate(); auto userInputMapper = DependencyManager::get(); if (name == KeyboardMouseDevice::NAME) { - userInputMapper->registerDevice(dynamic_cast(inputPlugin.get())->getInputDevice()); + userInputMapper->registerDevice(std::dynamic_pointer_cast(inputPlugin)->getInputDevice()); } inputPlugin->pluginUpdate(0, false); }