From 88a312f992ffa39eb71b69a88fb69fc4d7f0869c Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 26 Oct 2015 16:28:18 -0700 Subject: [PATCH 01/12] add functional js library... mmm, curry --- examples/libraries/fjs.js | 313 +++++++++++++++++++++++++++++++ examples/libraries/fjsExample.js | 9 + 2 files changed, 322 insertions(+) create mode 100644 examples/libraries/fjs.js create mode 100644 examples/libraries/fjsExample.js diff --git a/examples/libraries/fjs.js b/examples/libraries/fjs.js new file mode 100644 index 0000000000..738e9ae60f --- /dev/null +++ b/examples/libraries/fjs.js @@ -0,0 +1,313 @@ + loadFJS = function(){ + return fjs(); +} + +var fjs = function() { + "use strict"; + + var fjs = {}, + hardReturn = "hardReturn;"; + + var lambda = function(exp) { + if (!fjs.isString(exp)) { + return; + } + + var parts = exp.match(/(.*)\s*[=-]>\s*(.*)/); + parts.shift(); + + var params = parts.shift() + .replace(/^\s*|\s(?=\s)|\s*$|,/g, "").split(" "); + var body = parts.shift(); + + parts = ((!/\s*return\s+/.test(body)) ? "return " : "") + body; + params.push(parts); + + return Function.apply({}, params); + }; + + var sliceArgs = function(args) { + return args.length > 0 ? [].slice.call(args, 0) : []; + }; + + fjs.isFunction = function(obj) { + return !!(obj && obj.constructor && obj.call && obj.apply); + }; + + fjs.isObject = function(obj) { + return fjs.isFunction(obj) || (!!obj && typeof(obj) === "object"); + }; + + fjs.isArray = function(obj) { + return Object.prototype.toString.call(obj) === "[object Array]"; + }; + + var checkFunction = function(func) { + if (!fjs.isFunction(func)) { + func = lambda(func); + if (!fjs.isFunction(func)) { + throw "fjs Error: Invalid function"; + } + } + return func; + }; + + fjs.curry = function(func) { + func = checkFunction(func); + return function inner() { + var _args = sliceArgs(arguments); + if (_args.length === func.length) { + return func.apply(null, _args); + } else if (_args.length > func.length) { + var initial = func.apply(null, _args); + return fjs.fold(func, initial, _args.slice(func.length)); + } else { + return function() { + var args = sliceArgs(arguments); + return inner.apply(null, _args.concat(args)); + }; + } + }; + }; + + fjs.each = fjs.curry(function(iterator, items) { + iterator = checkFunction(iterator); + if (!fjs.exists(items) || !fjs.isArray(items)) { + return; + } + for (var i = 0; i < items.length; i += 1) { + if (iterator.call(null, items[i], i) === hardReturn) { + return; + } + } + }); + + fjs.map = fjs.curry(function(iterator, items) { + iterator = checkFunction(iterator); + var mapped = []; + fjs.each(function() { + mapped.push(iterator.apply(null, arguments)); + }, items); + return mapped; + }); + + fjs.fold = fjs.foldl = fjs.curry(function(iterator, cumulate, items) { + iterator = checkFunction(iterator); + fjs.each(function(item, i) { + cumulate = iterator.call(null, cumulate, item, i); + }, items); + return cumulate; + }); + + fjs.reduce = fjs.reducel = fjs.foldll = fjs.curry(function(iterator, items) { + iterator = checkFunction(iterator); + var cumulate = items[0]; + items.shift(); + return fjs.fold(iterator, cumulate, items); + }); + + fjs.clone = function(items) { + var clone = []; + fjs.each(function(item) { + clone.push(item); + }, items); + return clone; + }; + + fjs.first = fjs.head = fjs.take = fjs.curry(function(iterator, items) { + iterator = checkFunction(iterator); + var first; + fjs.each(function(item) { + if (iterator.call(null, item)) { + first = item; + return hardReturn; + } + }, items); + return first; + }); + + fjs.rest = fjs.tail = fjs.drop = fjs.curry(function(iterator, items) { + var result = fjs.select(iterator, items); + result.shift(); + return result; + }); + + fjs.last = fjs.curry(function(iterator, items) { + var itemsClone = fjs.clone(items); + return fjs.first(iterator, itemsClone.reverse()); + }); + + fjs.every = fjs.all = fjs.curry(function(iterator, items) { + iterator = checkFunction(iterator); + var isEvery = true; + fjs.each(function(item) { + if (!iterator.call(null, item)) { + isEvery = false; + return hardReturn; + } + }, items); + return isEvery; + }); + + fjs.any = fjs.contains = fjs.curry(function(iterator, items) { + iterator = checkFunction(iterator); + var isAny = false; + fjs.each(function(item) { + if (iterator.call(null, item)) { + isAny = true; + return hardReturn; + } + }, items); + return isAny; + }); + + fjs.select = fjs.filter = fjs.curry(function(iterator, items) { + iterator = checkFunction(iterator); + var filtered = []; + fjs.each(function(item) { + if (iterator.call(null, item)) { + filtered.push(item); + } + }, items); + return filtered; + }); + + fjs.best = fjs.curry(function(iterator, items) { + iterator = checkFunction(iterator); + var compare = function(arg1, arg2) { + return iterator.call(this, arg1, arg2) ? + arg1 : arg2; + }; + return fjs.reduce(compare, items); + }); + + fjs._while = fjs.curry(function(iterator, items) { + iterator = checkFunction(iterator); + var result = []; + fjs.each(function(item) { + if (iterator.call(null, item)) { + result.push(item); + } else { + return hardReturn; + } + }, items); + return result; + }); + + fjs.compose = function(funcs) { + var anyInvalid = fjs.any(function(func) { + return !fjs.isFunction(func); + }); + funcs = sliceArgs(arguments).reverse(); + if (anyInvalid(funcs)) { + throw "fjs Error: Invalid function to compose"; + } + return function() { + var args = arguments; + var applyEach = fjs.each(function(func) { + args = [func.apply(null, args)]; + }); + applyEach(funcs); + return args[0]; + }; + }; + + fjs.partition = fjs.curry(function(iterator, items) { + iterator = checkFunction(iterator); + var truthy = [], + falsy = []; + fjs.each(function(item) { + (iterator.call(null, item) ? truthy : falsy).push(item); + }, items); + return [truthy, falsy]; + }); + + fjs.group = fjs.curry(function(iterator, items) { + iterator = checkFunction(iterator); + var result = {}; + var group; + fjs.each(function(item) { + group = iterator.call(null, item); + result[group] = result[group] || []; + result[group].push(item); + }, items); + return result; + }); + + fjs.shuffle = function(items) { + var j, t; + fjs.each(function(item, i) { + j = Math.floor(Math.random() * (i + 1)); + t = items[i]; + items[i] = items[j]; + items[j] = t; + }, items); + return items; + }; + + fjs.toArray = function(obj) { + return fjs.map(function(key) { + return [key, obj[key]]; + }, Object.keys(obj)); + }; + + fjs.apply = fjs.curry(function(func, items) { + var args = []; + if (fjs.isArray(func)) { + args = [].slice.call(func, 1); + func = func[0]; + } + return fjs.map(function(item) { + return item[func].apply(item, args); + }, items); + }); + + fjs.assign = fjs.extend = fjs.curry(function(obj1, obj2) { + fjs.each(function(key) { + obj2[key] = obj1[key]; + }, Object.keys(obj1)); + return obj2; + }); + + fjs.prop = function(prop) { + return function(obj) { + return obj[prop]; + }; + }; + + fjs.pluck = fjs.curry(function(prop, items) { + return fjs.map(fjs.prop(prop), items); + }); + + fjs.nub = fjs.unique = fjs.distinct = fjs.curry(function(comparator, items) { + var unique = items.length > 0 ? [items[0]] : []; + + fjs.each(function(item) { + if (!fjs.any(fjs.curry(comparator)(item), unique)) { + unique[unique.length] = item; + } + }, items); + + return unique; + }); + + fjs.exists = function(obj) { + return obj != null; // jshint ignore:line + }; + + fjs.truthy = function(obj) { + return fjs.exists(obj) && obj !== false; + }; + + fjs.falsy = function(obj) { + return !fjs.truthy(obj); + }; + + fjs.each(function(type) { + fjs["is" + type] = function(obj) { + return Object.prototype.toString.call(obj) === "[object " + type + "]"; + }; + }, ["Arguments", "Date", "Number", "RegExp", "String"]); + + return fjs; +} \ No newline at end of file diff --git a/examples/libraries/fjsExample.js b/examples/libraries/fjsExample.js new file mode 100644 index 0000000000..486090a352 --- /dev/null +++ b/examples/libraries/fjsExample.js @@ -0,0 +1,9 @@ +Script.include('fjs.js'); +var fjs = loadFJS(); + +var concatenate = fjs.curry(function(word1, word2) { + return word1 + " " + word2; +}); +var concatenateHello = concatenate("Hello"); +var hi = concatenateHello("World"); +print('anyone listenig?' + hi) \ No newline at end of file From a1cd4a31cbb2786684f7de47b7a65f16c7686606 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 26 Oct 2015 16:41:03 -0700 Subject: [PATCH 02/12] fix typo --- examples/libraries/fjsExample.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/libraries/fjsExample.js b/examples/libraries/fjsExample.js index 486090a352..78960826fd 100644 --- a/examples/libraries/fjsExample.js +++ b/examples/libraries/fjsExample.js @@ -6,4 +6,4 @@ var concatenate = fjs.curry(function(word1, word2) { }); var concatenateHello = concatenate("Hello"); var hi = concatenateHello("World"); -print('anyone listenig?' + hi) \ No newline at end of file +print('anyone listening? ' + hi) \ No newline at end of file From d78fe089f64f7682e13c87cc8fc794b5b846196b Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 26 Oct 2015 17:00:54 -0700 Subject: [PATCH 03/12] add mit license --- examples/libraries/fjs.js | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/examples/libraries/fjs.js b/examples/libraries/fjs.js index 738e9ae60f..f2e6c48f59 100644 --- a/examples/libraries/fjs.js +++ b/examples/libraries/fjs.js @@ -1,3 +1,31 @@ +/* + +http://functionaljs.com/ + +https://github.com/leecrossley/functional-js/ + +The MIT License (MIT) +Copyright © 2015 Lee Crossley + +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. +*/ + loadFJS = function(){ return fjs(); } From 017e42bbc25478ab9ad8c1f733ea617ed42cccdf Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 2 Nov 2015 08:27:51 -0800 Subject: [PATCH 04/12] Update sixense SDK --- cmake/externals/sixense/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/externals/sixense/CMakeLists.txt b/cmake/externals/sixense/CMakeLists.txt index 07cf1c1163..8ae18e638a 100644 --- a/cmake/externals/sixense/CMakeLists.txt +++ b/cmake/externals/sixense/CMakeLists.txt @@ -6,8 +6,8 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) ExternalProject_Add( ${EXTERNAL_NAME} - URL http://hifi-public.s3.amazonaws.com/dependencies/SixenseSDK_071615.zip - URL_MD5 752a3901f334124e9cffc2ba4136ef7d + URL https://hifi-public.s3.amazonaws.com/dependencies/SixenseSDK_102215.zip + URL_MD5 93c3a6795cce777a0f472b09532935f1 CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" From b00f572e1a278e4eadc7b0947fe867ebdf0658c5 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 2 Nov 2015 09:21:15 -0800 Subject: [PATCH 05/12] add mouseClicked inputs vs mouseButton inputs, make right-click work without right button drag --- .../resources/controllers/keyboardMouse.json | 19 +++++----- .../src/input-plugins/KeyboardMouseDevice.cpp | 37 +++++++++++++++---- .../src/input-plugins/KeyboardMouseDevice.h | 7 +++- 3 files changed, 46 insertions(+), 17 deletions(-) diff --git a/interface/resources/controllers/keyboardMouse.json b/interface/resources/controllers/keyboardMouse.json index 8af6b1dc98..f99472b0e9 100644 --- a/interface/resources/controllers/keyboardMouse.json +++ b/interface/resources/controllers/keyboardMouse.json @@ -4,8 +4,8 @@ { "from": "Keyboard.A", "when": "Keyboard.Shift", "to": "Actions.LATERAL_LEFT" }, { "from": "Keyboard.D", "when": "Keyboard.Shift", "to": "Actions.LATERAL_RIGHT" }, - { "from": "Keyboard.A", "when": "Keyboard.RightMouseClick", "to": "Actions.LATERAL_LEFT" }, - { "from": "Keyboard.D", "when": "Keyboard.RightMouseClick", "to": "Actions.LATERAL_RIGHT" }, + { "from": "Keyboard.A", "when": "Keyboard.RightMouseButton", "to": "Actions.LATERAL_LEFT" }, + { "from": "Keyboard.D", "when": "Keyboard.RightMouseButton", "to": "Actions.LATERAL_RIGHT" }, { "from": "Keyboard.E", "when": "Keyboard.Shift", "to": "Actions.BOOM_IN", "filters": [ { "type": "scale", "scale": 0.05 } ] }, { "from": "Keyboard.C", "when": "Keyboard.Shift", "to": "Actions.BOOM_OUT", "filters": [ { "type": "scale", "scale": 0.05 } ] }, { "from": "Keyboard.S", "when": "Keyboard.Shift", "to": "Actions.PITCH_DOWN" }, @@ -13,7 +13,7 @@ { "from": { "makeAxis" : ["Keyboard.MouseMoveLeft", "Keyboard.MouseMoveRight"] }, - "when": [ "Application.InHMD", "Application.ComfortMode", "Keyboard.RightMouseClick" ], + "when": [ "Application.InHMD", "Application.ComfortMode", "Keyboard.RightMouseButton" ], "to": "Actions.StepYaw", "filters": [ @@ -46,7 +46,7 @@ }, { "from": { "makeAxis" : ["Keyboard.MouseMoveLeft", "Keyboard.MouseMoveRight"] }, - "when": "Keyboard.RightMouseClick", + "when": "Keyboard.RightMouseButton", "to": "Actions.Yaw" }, @@ -55,8 +55,8 @@ { "from": "Keyboard.C", "to": "Actions.VERTICAL_DOWN" }, { "from": "Keyboard.E", "to": "Actions.VERTICAL_UP" }, - { "from": "Keyboard.Left", "when": "Keyboard.RightMouseClick", "to": "Actions.LATERAL_LEFT" }, - { "from": "Keyboard.Right", "when": "Keyboard.RightMouseClick", "to": "Actions.LATERAL_RIGHT" }, + { "from": "Keyboard.Left", "when": "Keyboard.RightMouseButton", "to": "Actions.LATERAL_LEFT" }, + { "from": "Keyboard.Right", "when": "Keyboard.RightMouseButton", "to": "Actions.LATERAL_RIGHT" }, { "from": "Keyboard.Left", "when": "Keyboard.Shift", "to": "Actions.LATERAL_LEFT" }, { "from": "Keyboard.Right", "when": "Keyboard.Shift", "to": "Actions.LATERAL_RIGHT" }, { "from": "Keyboard.Down", "when": "Keyboard.Shift", "to": "Actions.PITCH_DOWN" }, @@ -68,8 +68,8 @@ { "from": "Keyboard.PgDown", "to": "Actions.VERTICAL_DOWN" }, { "from": "Keyboard.PgUp", "to": "Actions.VERTICAL_UP" }, - { "from": "Keyboard.MouseMoveUp", "when": "Keyboard.RightMouseClick", "to": "Actions.PITCH_UP" }, - { "from": "Keyboard.MouseMoveDown", "when": "Keyboard.RightMouseClick", "to": "Actions.PITCH_DOWN" }, + { "from": "Keyboard.MouseMoveUp", "when": "Keyboard.RightMouseButton", "to": "Actions.PITCH_UP" }, + { "from": "Keyboard.MouseMoveDown", "when": "Keyboard.RightMouseButton", "to": "Actions.PITCH_DOWN" }, { "from": "Keyboard.TouchpadDown", "to": "Actions.PITCH_DOWN" }, { "from": "Keyboard.TouchpadUp", "to": "Actions.PITCH_UP" }, @@ -81,6 +81,7 @@ { "from": "Keyboard.Space", "to": "Actions.SHIFT" }, { "from": "Keyboard.R", "to": "Actions.ACTION1" }, - { "from": "Keyboard.T", "to": "Actions.ACTION2" } + { "from": "Keyboard.T", "to": "Actions.ACTION2" }, + { "from": "Keyboard.RightMouseClicked", "to": "Actions.ContextMenu" } ] } diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp index 9cd510c521..42081aca30 100755 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp @@ -59,11 +59,25 @@ void KeyboardMouseDevice::mousePressEvent(QMouseEvent* event, unsigned int devic // key pressed again ? without catching the release event ? } _lastCursor = event->pos(); + _mousePressAt = event->pos(); + + eraseMouseClicked(); } void KeyboardMouseDevice::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) { auto input = makeInput((Qt::MouseButton) event->button()); _buttonPressedMap.erase(input.getChannel()); + + if (_mousePressAt == event->pos()) { + auto input = makeInput((Qt::MouseButton) event->button(), true); // clicked + auto result = _buttonPressedMap.insert(input.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()); } void KeyboardMouseDevice::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) { @@ -77,6 +91,8 @@ void KeyboardMouseDevice::mouseMoveEvent(QMouseEvent* event, unsigned int device _axisStateMap[makeInput(MOUSE_AXIS_Y_NEG).getChannel()] = (currentMove.y() > 0 ? currentMove.y() : 0.0f); _lastCursor = currentPos; + + eraseMouseClicked(); } void KeyboardMouseDevice::wheelEvent(QWheelEvent* event) { @@ -138,14 +154,17 @@ controller::Input KeyboardMouseDevice::makeInput(Qt::Key code) const { return controller::Input(_deviceID, shortCode, controller::ChannelType::BUTTON); } -controller::Input KeyboardMouseDevice::makeInput(Qt::MouseButton code) const { +controller::Input KeyboardMouseDevice::makeInput(Qt::MouseButton code, bool quickClicked) const { switch (code) { case Qt::LeftButton: - return controller::Input(_deviceID, MOUSE_BUTTON_LEFT, controller::ChannelType::BUTTON); + return controller::Input(_deviceID, quickClicked ? MOUSE_BUTTON_LEFT_QUICK_CLICK : + MOUSE_BUTTON_LEFT, controller::ChannelType::BUTTON); case Qt::RightButton: - return controller::Input(_deviceID, MOUSE_BUTTON_RIGHT, controller::ChannelType::BUTTON); + return controller::Input(_deviceID, quickClicked ? MOUSE_BUTTON_RIGHT_QUICK_CLICK : + MOUSE_BUTTON_RIGHT, controller::ChannelType::BUTTON); case Qt::MiddleButton: - return controller::Input(_deviceID, MOUSE_BUTTON_MIDDLE, controller::ChannelType::BUTTON); + return controller::Input(_deviceID, quickClicked ? MOUSE_BUTTON_MIDDLE_QUICK_CLICK : + MOUSE_BUTTON_MIDDLE, controller::ChannelType::BUTTON); default: return controller::Input(); }; @@ -182,9 +201,13 @@ controller::Input::NamedVector KeyboardMouseDevice::getAvailableInputs() const { availableInputs.append(Input::NamedPair(makeInput(Qt::Key_PageUp), QKeySequence(Qt::Key_PageUp).toString())); availableInputs.append(Input::NamedPair(makeInput(Qt::Key_PageDown), QKeySequence(Qt::Key_PageDown).toString())); - availableInputs.append(Input::NamedPair(makeInput(Qt::LeftButton), "LeftMouseClick")); - availableInputs.append(Input::NamedPair(makeInput(Qt::MiddleButton), "MiddleMouseClick")); - availableInputs.append(Input::NamedPair(makeInput(Qt::RightButton), "RightMouseClick")); + availableInputs.append(Input::NamedPair(makeInput(Qt::LeftButton), "LeftMouseButton")); + availableInputs.append(Input::NamedPair(makeInput(Qt::MiddleButton), "MiddleMouseButton")); + availableInputs.append(Input::NamedPair(makeInput(Qt::RightButton), "RightMouseButton")); + + availableInputs.append(Input::NamedPair(makeInput(Qt::LeftButton, true), "LeftMouseClicked")); + availableInputs.append(Input::NamedPair(makeInput(Qt::MiddleButton, true), "MiddleMouseClicked")); + availableInputs.append(Input::NamedPair(makeInput(Qt::RightButton, true), "RightMouseClicked")); availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_X_POS), "MouseMoveRight")); availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_X_NEG), "MouseMoveLeft")); diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h index 1ff77d2dce..75a1159b92 100644 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h @@ -37,6 +37,9 @@ public: MOUSE_BUTTON_LEFT = KEYBOARD_LAST + 1, MOUSE_BUTTON_RIGHT, MOUSE_BUTTON_MIDDLE, + MOUSE_BUTTON_LEFT_QUICK_CLICK, + MOUSE_BUTTON_RIGHT_QUICK_CLICK, + MOUSE_BUTTON_MIDDLE_QUICK_CLICK, }; enum MouseAxisChannel { @@ -83,6 +86,7 @@ public: void mouseMoveEvent(QMouseEvent* event, unsigned int deviceID = 0); void mousePressEvent(QMouseEvent* event, unsigned int deviceID = 0); void mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID = 0); + void eraseMouseClicked(); void touchBeginEvent(const QTouchEvent* event); void touchEndEvent(const QTouchEvent* event); @@ -92,7 +96,7 @@ public: // 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) const; + controller::Input makeInput(Qt::MouseButton code, bool quickClicked = false) const; controller::Input makeInput(MouseAxisChannel axis) const; controller::Input makeInput(TouchAxisChannel axis) const; controller::Input makeInput(TouchButtonChannel button) const; @@ -101,6 +105,7 @@ public: protected: QPoint _lastCursor; + QPoint _mousePressAt; glm::vec2 _lastTouch; bool _isTouching = false; From c3a78ed151a60dd9530765e631f6270ca4e1b887 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 2 Nov 2015 09:27:35 -0800 Subject: [PATCH 06/12] cleanup --- .../src/input-plugins/KeyboardMouseDevice.cpp | 12 +++++++----- .../src/input-plugins/KeyboardMouseDevice.h | 6 +++--- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp index 42081aca30..247e08ada1 100755 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp @@ -68,9 +68,11 @@ void KeyboardMouseDevice::mouseReleaseEvent(QMouseEvent* event, unsigned int dev auto input = makeInput((Qt::MouseButton) event->button()); _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()) { - auto input = makeInput((Qt::MouseButton) event->button(), true); // clicked - auto result = _buttonPressedMap.insert(input.getChannel()); + _buttonPressedMap.insert(makeInput((Qt::MouseButton) event->button(), true).getChannel()); } } @@ -157,13 +159,13 @@ controller::Input KeyboardMouseDevice::makeInput(Qt::Key code) const { controller::Input KeyboardMouseDevice::makeInput(Qt::MouseButton code, bool quickClicked) const { switch (code) { case Qt::LeftButton: - return controller::Input(_deviceID, quickClicked ? MOUSE_BUTTON_LEFT_QUICK_CLICK : + return controller::Input(_deviceID, quickClicked ? MOUSE_BUTTON_LEFT_CLICKED : MOUSE_BUTTON_LEFT, controller::ChannelType::BUTTON); case Qt::RightButton: - return controller::Input(_deviceID, quickClicked ? MOUSE_BUTTON_RIGHT_QUICK_CLICK : + return controller::Input(_deviceID, quickClicked ? MOUSE_BUTTON_RIGHT_CLICKED : MOUSE_BUTTON_RIGHT, controller::ChannelType::BUTTON); case Qt::MiddleButton: - return controller::Input(_deviceID, quickClicked ? MOUSE_BUTTON_MIDDLE_QUICK_CLICK : + return controller::Input(_deviceID, quickClicked ? MOUSE_BUTTON_MIDDLE_CLICKED : MOUSE_BUTTON_MIDDLE, controller::ChannelType::BUTTON); default: return controller::Input(); diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h index 75a1159b92..b6681e2ae0 100644 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h @@ -37,9 +37,9 @@ public: MOUSE_BUTTON_LEFT = KEYBOARD_LAST + 1, MOUSE_BUTTON_RIGHT, MOUSE_BUTTON_MIDDLE, - MOUSE_BUTTON_LEFT_QUICK_CLICK, - MOUSE_BUTTON_RIGHT_QUICK_CLICK, - MOUSE_BUTTON_MIDDLE_QUICK_CLICK, + MOUSE_BUTTON_LEFT_CLICKED, + MOUSE_BUTTON_RIGHT_CLICKED, + MOUSE_BUTTON_MIDDLE_CLICKED, }; enum MouseAxisChannel { From d9dd045886fba3a416eda545a74a3be5bb5d55ad Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 2 Nov 2015 09:29:09 -0800 Subject: [PATCH 07/12] better parameter name --- .../src/input-plugins/KeyboardMouseDevice.cpp | 8 ++++---- .../input-plugins/src/input-plugins/KeyboardMouseDevice.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp index 247e08ada1..2157a3a010 100755 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp @@ -156,16 +156,16 @@ controller::Input KeyboardMouseDevice::makeInput(Qt::Key code) const { return controller::Input(_deviceID, shortCode, controller::ChannelType::BUTTON); } -controller::Input KeyboardMouseDevice::makeInput(Qt::MouseButton code, bool quickClicked) const { +controller::Input KeyboardMouseDevice::makeInput(Qt::MouseButton code, bool clicked) const { switch (code) { case Qt::LeftButton: - return controller::Input(_deviceID, quickClicked ? MOUSE_BUTTON_LEFT_CLICKED : + return controller::Input(_deviceID, clicked ? MOUSE_BUTTON_LEFT_CLICKED : MOUSE_BUTTON_LEFT, controller::ChannelType::BUTTON); case Qt::RightButton: - return controller::Input(_deviceID, quickClicked ? MOUSE_BUTTON_RIGHT_CLICKED : + return controller::Input(_deviceID, clicked ? MOUSE_BUTTON_RIGHT_CLICKED : MOUSE_BUTTON_RIGHT, controller::ChannelType::BUTTON); case Qt::MiddleButton: - return controller::Input(_deviceID, quickClicked ? MOUSE_BUTTON_MIDDLE_CLICKED : + return controller::Input(_deviceID, clicked ? MOUSE_BUTTON_MIDDLE_CLICKED : MOUSE_BUTTON_MIDDLE, controller::ChannelType::BUTTON); default: return controller::Input(); diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h index b6681e2ae0..5d86821db1 100644 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h @@ -96,7 +96,7 @@ public: // 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 quickClicked = false) 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; From c381da24a7a7871bc9e7ccf6093979a6a08a27c1 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 2 Nov 2015 10:42:06 -0800 Subject: [PATCH 08/12] get interface building on my linux box --- libraries/gl/src/gl/GlWindow.h | 2 +- .../oculusLegacy/src/OculusLegacyDisplayPlugin.cpp | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/libraries/gl/src/gl/GlWindow.h b/libraries/gl/src/gl/GlWindow.h index d62d891c12..4956177725 100644 --- a/libraries/gl/src/gl/GlWindow.h +++ b/libraries/gl/src/gl/GlWindow.h @@ -11,7 +11,7 @@ #define hifi_GlWindow_h #include -#include +#include class QOpenGLContext; class QOpenGLDebugLogger; diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp index d8dc3667ae..3898d586ad 100644 --- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp @@ -9,15 +9,15 @@ #include -#include -#include +#include +#include #include #include #include -#include -#include -#include -#include +#include +#include +#include +#include #include #include From fd557c4e529ef06b36500d2e73d399bb38a103c9 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 30 Oct 2015 18:59:33 -0700 Subject: [PATCH 09/12] route HMD-following through the physics simulation --- interface/src/avatar/MyAvatar.cpp | 191 +++++++++++------------- interface/src/avatar/MyAvatar.h | 15 +- libraries/shared/src/AtRestDetector.cpp | 24 ++- libraries/shared/src/AtRestDetector.h | 11 +- 4 files changed, 125 insertions(+), 116 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 9dcd77c689..e3528f3287 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -157,6 +157,8 @@ void MyAvatar::reset(bool andReload) { // Reset dynamic state. _wasPushing = _isPushing = _isBraking = _billboardValid = false; _isFollowingHMD = false; + _hmdFollowVelocity = Vectors::ZERO; + _hmdFollowSpeed = 0.0f; _skeletonModel.reset(); getHead()->reset(); _targetVelocity = glm::vec3(0.0f); @@ -332,25 +334,6 @@ glm::mat4 MyAvatar::getSensorToWorldMatrix() const { return _sensorToWorldMatrix; } -// returns true if pos is OUTSIDE of the vertical capsule -// where the middle cylinder length is defined by capsuleLen and the radius by capsuleRad. -static bool pointIsOutsideCapsule(const glm::vec3& pos, float capsuleLen, float capsuleRad) { - const float halfCapsuleLen = capsuleLen / 2.0f; - if (fabs(pos.y) <= halfCapsuleLen) { - // cylinder check for middle capsule - glm::vec2 horizPos(pos.x, pos.z); - return glm::length(horizPos) > capsuleRad; - } else if (pos.y > halfCapsuleLen) { - glm::vec3 center(0.0f, halfCapsuleLen, 0.0f); - return glm::length(center - pos) > capsuleRad; - } else if (pos.y < halfCapsuleLen) { - glm::vec3 center(0.0f, -halfCapsuleLen, 0.0f); - return glm::length(center - pos) > capsuleRad; - } else { - return false; - } -} - // Pass a recent sample of the HMD to the avatar. // This can also update the avatar's position to follow the HMD // as it moves through the world. @@ -359,102 +342,58 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { _hmdSensorMatrix = hmdSensorMatrix; _hmdSensorPosition = extractTranslation(hmdSensorMatrix); _hmdSensorOrientation = glm::quat_cast(hmdSensorMatrix); +} - // calc deltaTime - auto now = usecTimestampNow(); - auto deltaUsecs = now - _lastUpdateFromHMDTime; - _lastUpdateFromHMDTime = now; - double actualDeltaTime = (double)deltaUsecs / (double)USECS_PER_SECOND; - const float BIGGEST_DELTA_TIME_SECS = 0.25f; - float deltaTime = glm::clamp((float)actualDeltaTime, 0.0f, BIGGEST_DELTA_TIME_SECS); - - bool hmdIsAtRest = _hmdAtRestDetector.update(deltaTime, _hmdSensorPosition, _hmdSensorOrientation); - - // It can be more accurate/smooth to use velocity rather than position, - // but some modes (e.g., hmd standing) update position without updating velocity. - // So, let's create our own workingVelocity from the worldPosition... - glm::vec3 positionDelta = getPosition() - _lastPosition; - glm::vec3 workingVelocity = positionDelta / deltaTime; - _lastPosition = getPosition(); - - const float MOVE_ENTER_SPEED_THRESHOLD = 0.2f; // m/sec - const float MOVE_EXIT_SPEED_THRESHOLD = 0.07f; // m/sec +void MyAvatar::updateHMDFollowVelocity() { bool isMoving; if (_lastIsMoving) { - isMoving = glm::length(workingVelocity) >= MOVE_EXIT_SPEED_THRESHOLD; + const float MOVE_EXIT_SPEED_THRESHOLD = 0.07f; // m/sec + isMoving = glm::length(_velocity) >= MOVE_EXIT_SPEED_THRESHOLD; } else { - isMoving = glm::length(workingVelocity) > MOVE_ENTER_SPEED_THRESHOLD; + const float MOVE_ENTER_SPEED_THRESHOLD = 0.2f; // m/sec + isMoving = glm::length(_velocity) > MOVE_ENTER_SPEED_THRESHOLD; } bool justStartedMoving = (_lastIsMoving != isMoving) && isMoving; _lastIsMoving = isMoving; - if (shouldFollowHMD() || hmdIsAtRest || justStartedMoving) { - beginFollowingHMD(); - } - - followHMD(deltaTime); -} - -glm::vec3 MyAvatar::getHMDCorrectionVelocity() const { - // TODO: impelement this - return Vectors::ZERO; -} - -void MyAvatar::beginFollowingHMD() { - // begin homing toward derived body position. - if (!_isFollowingHMD) { + bool hmdIsAtRest = _hmdAtRestDetector.update(_hmdSensorPosition, _hmdSensorOrientation); + if (hmdIsAtRest || justStartedMoving) { _isFollowingHMD = true; - _followHMDAlpha = 0.0f; } -} -bool MyAvatar::shouldFollowHMD() const { - if (!_isFollowingHMD) { - // define a vertical capsule - const float FOLLOW_HMD_CAPSULE_RADIUS = 0.2f; // meters - const float FOLLOW_HMD_CAPSULE_LENGTH = 0.05f; // length of the cylinder part of the capsule in meters. - - // detect if the derived body position is outside of a capsule around the _bodySensorMatrix - auto newBodySensorMatrix = deriveBodyFromHMDSensor(); - glm::vec3 localPoint = extractTranslation(newBodySensorMatrix) - extractTranslation(_bodySensorMatrix); - return pointIsOutsideCapsule(localPoint, FOLLOW_HMD_CAPSULE_LENGTH, FOLLOW_HMD_CAPSULE_RADIUS); + // compute offset to body's target position (in sensor-frame) + auto sensorBodyMatrix = deriveBodyFromHMDSensor(); + _hmdFollowOffset = extractTranslation(sensorBodyMatrix) - extractTranslation(_bodySensorMatrix); + if (_hmdFollowOffset.y < 0.0f) { + // don't pull the body DOWN to match the target (allow animation system to squat) + _hmdFollowOffset.y = 0.0f; } - return false; -} -void MyAvatar::followHMD(float deltaTime) { - if (_isFollowingHMD) { - - const float FOLLOW_HMD_DURATION = 0.5f; // seconds - - auto newBodySensorMatrix = deriveBodyFromHMDSensor(); - auto worldBodyMatrix = _sensorToWorldMatrix * newBodySensorMatrix; - glm::vec3 worldBodyPos = extractTranslation(worldBodyMatrix); - glm::quat worldBodyRot = glm::normalize(glm::quat_cast(worldBodyMatrix)); - - _followHMDAlpha += (1.0f / FOLLOW_HMD_DURATION) * deltaTime; - - if (_followHMDAlpha >= 1.0f) { - _isFollowingHMD = false; - nextAttitude(worldBodyPos, worldBodyRot); - _bodySensorMatrix = newBodySensorMatrix; - } else { - // interp position toward the desired pos - glm::vec3 pos = lerp(getPosition(), worldBodyPos, _followHMDAlpha); - glm::quat rot = glm::normalize(safeMix(getOrientation(), worldBodyRot, _followHMDAlpha)); - nextAttitude(pos, rot); - - // interp sensor matrix toward desired - glm::vec3 nextBodyPos = extractTranslation(newBodySensorMatrix); - glm::quat nextBodyRot = glm::normalize(glm::quat_cast(newBodySensorMatrix)); - glm::vec3 prevBodyPos = extractTranslation(_bodySensorMatrix); - glm::quat prevBodyRot = glm::normalize(glm::quat_cast(_bodySensorMatrix)); - pos = lerp(prevBodyPos, nextBodyPos, _followHMDAlpha); - rot = glm::normalize(safeMix(prevBodyRot, nextBodyRot, _followHMDAlpha)); - _bodySensorMatrix = createMatFromQuatAndPos(rot, pos); + bool needNewFollowSpeed = (_isFollowingHMD && _hmdFollowSpeed == 0.0f); + if (!needNewFollowSpeed) { + // check to see if offset has exceeded its threshold + float distance = glm::length(_hmdFollowOffset); + const float MAX_HMD_HIP_SHIFT = 0.2f; + if (distance > MAX_HMD_HIP_SHIFT) { + _isFollowingHMD = true; + needNewFollowSpeed = true; } } + if (_isFollowingHMD) { + // only bother to rotate into world frame if we're following + glm::quat sensorToWorldRotation = extractRotation(_sensorToWorldMatrix); + _hmdFollowOffset = sensorToWorldRotation * _hmdFollowOffset; + } + if (needNewFollowSpeed) { + // compute new velocity that will be used to resolve offset of hips from body + const float FOLLOW_HMD_DURATION = 0.5f; // seconds + _hmdFollowVelocity = (_hmdFollowOffset / FOLLOW_HMD_DURATION); + _hmdFollowSpeed = glm::length(_hmdFollowVelocity); + } else if (_isFollowingHMD) { + // compute new velocity (but not new speed) + _hmdFollowVelocity = _hmdFollowSpeed * glm::normalize(_hmdFollowOffset); + } } // best called at end of main loop, just before rendering. @@ -1356,7 +1295,8 @@ void MyAvatar::prepareForPhysicsSimulation() { relayDriveKeysToCharacterController(); _characterController.setTargetVelocity(getTargetVelocity()); _characterController.setAvatarPositionAndOrientation(getPosition(), getOrientation()); - _characterController.setHMDVelocity(getHMDCorrectionVelocity()); + updateHMDFollowVelocity(); + _characterController.setHMDVelocity(_hmdFollowVelocity); } void MyAvatar::harvestResultsFromPhysicsSimulation() { @@ -1364,9 +1304,54 @@ void MyAvatar::harvestResultsFromPhysicsSimulation() { glm::quat orientation = getOrientation(); _characterController.getAvatarPositionAndOrientation(position, orientation); nextAttitude(position, orientation); - setVelocity(_characterController.getLinearVelocity()); - // TODO: harvest HMD shift here - //glm::vec3 hmdShift = _characterController.getHMDShift(); + if (_isFollowingHMD) { + setVelocity(_characterController.getLinearVelocity() + _hmdFollowVelocity); + glm::vec3 hmdShift = _characterController.getHMDShift(); + adjustSensorTransform(hmdShift); + } else { + setVelocity(_characterController.getLinearVelocity()); + } +} + +void MyAvatar::adjustSensorTransform(glm::vec3 hmdShift) { + // compute blendFactor of latest hmdShift + // which we'll use to blend the rotation part + float blendFactor = 1.0f; + float shiftLength = glm::length(hmdShift); + if (shiftLength > 1.0e-5f) { + float offsetLength = glm::length(_hmdFollowOffset); + if (offsetLength > shiftLength) { + blendFactor = shiftLength / offsetLength; + } + } + + auto newBodySensorMatrix = deriveBodyFromHMDSensor(); + auto worldBodyMatrix = _sensorToWorldMatrix * newBodySensorMatrix; + glm::quat finalBodyRotation = glm::normalize(glm::quat_cast(worldBodyMatrix)); + if (blendFactor >= 0.99f) { + // the "adjustment" is more or less complete so stop following + _isFollowingHMD = false; + _hmdFollowSpeed = 0.0f; + // and slam the body's transform anyway to eliminate any slight errors + glm::vec3 finalBodyPosition = extractTranslation(worldBodyMatrix); + nextAttitude(finalBodyPosition, finalBodyRotation); + _bodySensorMatrix = newBodySensorMatrix; + } else { + // physics already did the positional blending for us + glm::vec3 newBodyPosition = getPosition(); + // but the rotational part must be done manually + glm::quat newBodyRotation = glm::normalize(safeMix(getOrientation(), finalBodyRotation, blendFactor)); + nextAttitude(newBodyPosition, newBodyRotation); + + // interp sensor matrix toward the desired + glm::vec3 prevPosition = extractTranslation(_bodySensorMatrix); + glm::quat prevRotation = glm::normalize(glm::quat_cast(_bodySensorMatrix)); + glm::vec3 nextPosition = extractTranslation(newBodySensorMatrix); + glm::quat nextRotation = glm::normalize(glm::quat_cast(newBodySensorMatrix)); + _bodySensorMatrix = createMatFromQuatAndPos( + glm::normalize(safeMix(prevRotation, nextRotation, blendFactor)), + lerp(prevPosition, nextPosition, blendFactor)); + } } QString MyAvatar::getScriptedMotorFrame() const { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 38c2c00382..ec33d22d8d 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -101,8 +101,6 @@ public: // as it moves through the world. void updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix); - glm::vec3 getHMDCorrectionVelocity() const; - // best called at end of main loop, just before rendering. // update sensor to world matrix from current body position and hmd sensor. // This is so the correct camera can be used for rendering. @@ -211,6 +209,7 @@ public: void prepareForPhysicsSimulation(); void harvestResultsFromPhysicsSimulation(); + void adjustSensorTransform(glm::vec3 hmdShift); const QString& getCollisionSoundURL() { return _collisionSoundURL; } void setCollisionSoundURL(const QString& url); @@ -316,9 +315,10 @@ private: const RecorderPointer getRecorder() const { return _recorder; } const PlayerPointer getPlayer() const { return _player; } - void beginFollowingHMD(); - bool shouldFollowHMD() const; - void followHMD(float deltaTime); + //void beginFollowingHMD(); + //bool shouldFollowHMD() const; + //void followHMD(float deltaTime); + void updateHMDFollowVelocity(); bool cameraInsideHead() const; @@ -395,6 +395,9 @@ private: // used to transform any sensor into world space, including the _hmdSensorMat, or hand controllers. glm::mat4 _sensorToWorldMatrix; + glm::vec3 _hmdFollowOffset { Vectors::ZERO }; + glm::vec3 _hmdFollowVelocity { Vectors::ZERO }; + float _hmdFollowSpeed { 0.0f }; bool _goToPending; glm::vec3 _goToPosition; @@ -415,9 +418,7 @@ private: bool _isFollowingHMD { false }; float _followHMDAlpha { 0.0f }; - quint64 _lastUpdateFromHMDTime { usecTimestampNow() }; AtRestDetector _hmdAtRestDetector; - glm::vec3 _lastPosition; bool _lastIsMoving { false }; quint64 _lastStepPulse { 0 }; bool _pulseUpdate { false }; diff --git a/libraries/shared/src/AtRestDetector.cpp b/libraries/shared/src/AtRestDetector.cpp index 5e623a005c..799450df94 100644 --- a/libraries/shared/src/AtRestDetector.cpp +++ b/libraries/shared/src/AtRestDetector.cpp @@ -1,5 +1,19 @@ +// +// AtRestDetector.cpp +// libraries/shared/src +// +// Created by Anthony Thibault on 10/6/2015 +// 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 "AtRestDetector.h" + +#include "NumericalConstants.h" #include "SharedLogging.h" +#include "SharedUtil.h" AtRestDetector::AtRestDetector(const glm::vec3& startPosition, const glm::quat& startRotation) { reset(startPosition, startRotation); @@ -12,9 +26,14 @@ void AtRestDetector::reset(const glm::vec3& startPosition, const glm::quat& star glm::quat ql = glm::log(startRotation); _quatLogAverage = glm::vec3(ql.x, ql.y, ql.z); _quatLogVariance = 0.0f; + _lastUpdateTime = usecTimestampNow(); + _isAtRest = false; } -bool AtRestDetector::update(float dt, const glm::vec3& position, const glm::quat& rotation) { +bool AtRestDetector::update(const glm::vec3& position, const glm::quat& rotation) { + uint64_t now = usecTimestampNow(); + float dt = (float)(_lastUpdateTime - now) / (float)USECS_PER_SECOND; + _lastUpdateTime = now; const float TAU = 1.0f; float delta = glm::min(dt / TAU, 1.0f); @@ -37,5 +56,6 @@ bool AtRestDetector::update(float dt, const glm::vec3& position, const glm::quat const float POSITION_VARIANCE_THRESHOLD = 0.001f; const float QUAT_LOG_VARIANCE_THRESHOLD = 0.00002f; - return _positionVariance < POSITION_VARIANCE_THRESHOLD && _quatLogVariance < QUAT_LOG_VARIANCE_THRESHOLD; + _isAtRest = _positionVariance < POSITION_VARIANCE_THRESHOLD && _quatLogVariance < QUAT_LOG_VARIANCE_THRESHOLD; + return _isAtRest; } diff --git a/libraries/shared/src/AtRestDetector.h b/libraries/shared/src/AtRestDetector.h index d82e54a692..525156de65 100644 --- a/libraries/shared/src/AtRestDetector.h +++ b/libraries/shared/src/AtRestDetector.h @@ -21,14 +21,17 @@ public: void reset(const glm::vec3& startPosition, const glm::quat& startRotation); // returns true if object is at rest, dt in assumed to be seconds. - bool update(float dt, const glm::vec3& position, const glm::quat& startRotation); + bool update(const glm::vec3& position, const glm::quat& startRotation); + + bool isAtRest() const { return _isAtRest; } protected: glm::vec3 _positionAverage; - float _positionVariance; - glm::vec3 _quatLogAverage; - float _quatLogVariance; + uint64_t _lastUpdateTime { 0 }; + float _positionVariance { 0.0f }; + float _quatLogVariance { 0.0f }; + bool _isAtRest { false }; }; #endif From 9799693135189f176e4b7009ac7143919328ecde Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 2 Nov 2015 09:17:51 -0800 Subject: [PATCH 10/12] remove extra whitespace --- interface/src/avatar/MyAvatar.cpp | 10 +++++----- libraries/shared/src/AtRestDetector.cpp | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index e3528f3287..95a494e4fd 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -250,7 +250,7 @@ void MyAvatar::simulate(float deltaTime) { { PerformanceTimer perfTimer("transform"); bool stepAction = false; - // When there are no step values, we zero out the last step pulse. + // When there are no step values, we zero out the last step pulse. // This allows a user to do faster snapping by tapping a control for (int i = STEP_TRANSLATE_X; !stepAction && i <= STEP_YAW; ++i) { if (_driveKeys[i] != 0.0f) { @@ -1297,7 +1297,7 @@ void MyAvatar::prepareForPhysicsSimulation() { _characterController.setAvatarPositionAndOrientation(getPosition(), getOrientation()); updateHMDFollowVelocity(); _characterController.setHMDVelocity(_hmdFollowVelocity); -} +} void MyAvatar::harvestResultsFromPhysicsSimulation() { glm::vec3 position = getPosition(); @@ -1338,7 +1338,7 @@ void MyAvatar::adjustSensorTransform(glm::vec3 hmdShift) { _bodySensorMatrix = newBodySensorMatrix; } else { // physics already did the positional blending for us - glm::vec3 newBodyPosition = getPosition(); + glm::vec3 newBodyPosition = getPosition(); // but the rotational part must be done manually glm::quat newBodyRotation = glm::normalize(safeMix(getOrientation(), finalBodyRotation, blendFactor)); nextAttitude(newBodyPosition, newBodyRotation); @@ -1453,7 +1453,7 @@ void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, fl getHead()->renderLookAts(renderArgs); } - if (renderArgs->_renderMode != RenderArgs::SHADOW_RENDER_MODE && + if (renderArgs->_renderMode != RenderArgs::SHADOW_RENDER_MODE && Menu::getInstance()->isOptionChecked(MenuOption::DisplayHandTargets)) { getHand()->renderHandTargets(renderArgs, true); } @@ -1932,7 +1932,7 @@ void MyAvatar::updateMotionBehaviorFromMenu() { QMetaObject::invokeMethod(this, "updateMotionBehaviorFromMenu"); return; } - + Menu* menu = Menu::getInstance(); if (menu->isOptionChecked(MenuOption::KeyboardMotorControl)) { _motionBehaviors |= AVATAR_MOTION_KEYBOARD_MOTOR_ENABLED; diff --git a/libraries/shared/src/AtRestDetector.cpp b/libraries/shared/src/AtRestDetector.cpp index 799450df94..d790e2b066 100644 --- a/libraries/shared/src/AtRestDetector.cpp +++ b/libraries/shared/src/AtRestDetector.cpp @@ -31,7 +31,7 @@ void AtRestDetector::reset(const glm::vec3& startPosition, const glm::quat& star } bool AtRestDetector::update(const glm::vec3& position, const glm::quat& rotation) { - uint64_t now = usecTimestampNow(); + uint64_t now = usecTimestampNow(); float dt = (float)(_lastUpdateTime - now) / (float)USECS_PER_SECOND; _lastUpdateTime = now; const float TAU = 1.0f; From a545d770d888f12b42dbde32df275783afb02236 Mon Sep 17 00:00:00 2001 From: "U-GAPOS\\andrew" Date: Mon, 2 Nov 2015 11:06:17 -0800 Subject: [PATCH 11/12] fix bug preventing avatar motion in 2D mode --- interface/src/avatar/MyAvatar.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 95a494e4fd..3bf1b79902 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1295,8 +1295,13 @@ void MyAvatar::prepareForPhysicsSimulation() { relayDriveKeysToCharacterController(); _characterController.setTargetVelocity(getTargetVelocity()); _characterController.setAvatarPositionAndOrientation(getPosition(), getOrientation()); - updateHMDFollowVelocity(); - _characterController.setHMDVelocity(_hmdFollowVelocity); + if (qApp->isHMDMode()) { + updateHMDFollowVelocity(); + _characterController.setHMDVelocity(_hmdFollowVelocity); + } else { + _characterController.setHMDVelocity(Vectors::ZERO); + _isFollowingHMD = false; + } } void MyAvatar::harvestResultsFromPhysicsSimulation() { From a77f888f1574589f802daa62b373499858e5d7cb Mon Sep 17 00:00:00 2001 From: "U-GAPOS\\andrew" Date: Mon, 2 Nov 2015 11:29:17 -0800 Subject: [PATCH 12/12] more correct HMD offset correction velocity --- interface/src/avatar/MyAvatar.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 3bf1b79902..00870c62c6 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -365,15 +365,16 @@ void MyAvatar::updateHMDFollowVelocity() { // compute offset to body's target position (in sensor-frame) auto sensorBodyMatrix = deriveBodyFromHMDSensor(); _hmdFollowOffset = extractTranslation(sensorBodyMatrix) - extractTranslation(_bodySensorMatrix); - if (_hmdFollowOffset.y < 0.0f) { + glm::vec3 truncatedOffset = _hmdFollowOffset; + if (truncatedOffset.y < 0.0f) { // don't pull the body DOWN to match the target (allow animation system to squat) - _hmdFollowOffset.y = 0.0f; + truncatedOffset.y = 0.0f; } bool needNewFollowSpeed = (_isFollowingHMD && _hmdFollowSpeed == 0.0f); if (!needNewFollowSpeed) { // check to see if offset has exceeded its threshold - float distance = glm::length(_hmdFollowOffset); + float distance = glm::length(truncatedOffset); const float MAX_HMD_HIP_SHIFT = 0.2f; if (distance > MAX_HMD_HIP_SHIFT) { _isFollowingHMD = true;