From 86abd9120ebffdf1f1bd314549e55bd412572bef Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Tue, 1 Nov 2016 22:16:56 +0100 Subject: [PATCH 01/29] Fixed menu out of view with HMD and Xbox controller --- .../controllers/handControllerPointer.js | 44 ++++++++++++++++++- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/scripts/system/controllers/handControllerPointer.js b/scripts/system/controllers/handControllerPointer.js index c2deaa2fac..26a8eb5ab8 100644 --- a/scripts/system/controllers/handControllerPointer.js +++ b/scripts/system/controllers/handControllerPointer.js @@ -203,14 +203,41 @@ function overlayFromWorldPoint(point) { return { x: horizontalPixels, y: verticalPixels }; } +function activeHudPoint2dGamePad() { + if(!HMD.active){ + return; + } + var headPosition = MyAvatar.getHeadPosition(); + var headDirection = Quat.getUp(Quat.multiply(MyAvatar.headOrientation, Quat.angleAxis(-90, { x: 1, y: 0, z: 0 }))); + + var hudPoint3d = calculateRayUICollisionPoint(headPosition, headDirection); + + if (!hudPoint3d) { + if (Menu.isOptionChecked("Overlays")) { // With our hud resetting strategy, hudPoint3d should be valid here + print('Controller is parallel to HUD'); // so let us know that our assumptions are wrong. + } + return; + } + var hudPoint2d = overlayFromWorldPoint(hudPoint3d); + + // We don't know yet if we'll want to make the cursor or laser visble, but we need to move it to see if + // it's pointing at a QML tool (aka system overlay). + setReticlePosition(hudPoint2d); + + return hudPoint2d; +} + function activeHudPoint2d(activeHand) { // if controller is valid, update reticle position and answer 2d point. Otherwise falsey. var controllerPose = getControllerWorldLocation(activeHand, true); // note: this will return head pose if hand pose is invalid (third eye) if (!controllerPose.valid) { + if(gamePad){ + return activeHudPoint2dGamePad(); + } return; // Controller is cradled. } var controllerPosition = controllerPose.position; var controllerDirection = Quat.getUp(controllerPose.rotation); - + var hudPoint3d = calculateRayUICollisionPoint(controllerPosition, controllerDirection); if (!hudPoint3d) { if (Menu.isOptionChecked("Overlays")) { // With our hud resetting strategy, hudPoint3d should be valid here @@ -350,6 +377,7 @@ var leftTrigger = new Trigger('left'); var rightTrigger = new Trigger('right'); var activeTrigger = rightTrigger; var activeHand = Controller.Standard.RightHand; +var gamePad = Controller.findDevice("GamePad"); var LEFT_HUD_LASER = 1; var RIGHT_HUD_LASER = 2; var BOTH_HUD_LASERS = LEFT_HUD_LASER + RIGHT_HUD_LASER; @@ -405,7 +433,7 @@ clickMapping.from(rightTrigger.full).when(isPointingAtOverlayStartedNonFullTrigg clickMapping.from(leftTrigger.full).when(isPointingAtOverlayStartedNonFullTrigger(leftTrigger)).to(Controller.Actions.ReticleClick); // The following is essentially like Left and Right versions of // clickMapping.from(Controller.Standard.RightSecondaryThumb).peek().to(Controller.Actions.ContextMenu); -// except that we first update the reticle position from the appropriate hand position, before invoking the ContextMenu. +// except that we first update the reticle position from the appropriate hand position, before invoking the . var wantsMenu = 0; clickMapping.from(function () { return wantsMenu; }).to(Controller.Actions.ContextMenu); clickMapping.from(Controller.Standard.RightSecondaryThumb).peek().to(function (clicked) { @@ -420,6 +448,11 @@ clickMapping.from(Controller.Standard.LeftSecondaryThumb).peek().to(function (cl } wantsMenu = clicked; }); +clickMapping.from(Controller.Hardware.GamePad.Back).peek().to(function () { + Script.setTimeout(function () { + activeHudPoint2dGamePad(); + }, 0); +}); clickMapping.from(Controller.Hardware.Keyboard.RightMouseClicked).peek().to(function () { // Allow the reticle depth to be set correctly: // Wait a tick for the context menu to be displayed, and then simulate a (non-hand-controller) mouse move @@ -472,13 +505,16 @@ function update() { expireMouseCursor(); clearSystemLaser(); } + //print("In updating HandControllerPointer"); updateSeeking(true); if (!handControllerLockOut.expired(now)) { return off(); // Let them use mouse in peace. } + //print("test2"); if (!Menu.isOptionChecked("First Person")) { return off(); // What to do? menus can be behind hand! } + //print("test3"); if ((!Window.hasFocus() && !HMD.active) || !Reticle.allowMouseCapture) { // In desktop it's pretty clear when another app is on top. In that case we bail, because // hand controllers might be sputtering "valid" data and that will keep someone from deliberately @@ -487,14 +523,18 @@ function update() { // other apps anyway. So in that case, we DO keep going even though we're not on top. (Fogbugz 1831.) return off(); // Don't mess with other apps or paused mouse activity } + leftTrigger.update(); rightTrigger.update(); if (!activeTrigger.state) { return off(); // No trigger } + if (getGrabCommunications()) { return off(); } + + var hudPoint2d = activeHudPoint2d(activeHand); if (!hudPoint2d) { return off(); From e9d10b609d3e50b02ab1089577b0887e6647113d Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Wed, 2 Nov 2016 00:05:38 +0100 Subject: [PATCH 02/29] removed bad commit --- scripts/system/controllers/handControllerPointer.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/scripts/system/controllers/handControllerPointer.js b/scripts/system/controllers/handControllerPointer.js index 26a8eb5ab8..77cc7603e5 100644 --- a/scripts/system/controllers/handControllerPointer.js +++ b/scripts/system/controllers/handControllerPointer.js @@ -204,7 +204,7 @@ function overlayFromWorldPoint(point) { } function activeHudPoint2dGamePad() { - if(!HMD.active){ + if (!HMD.active) { return; } var headPosition = MyAvatar.getHeadPosition(); @@ -227,6 +227,7 @@ function activeHudPoint2dGamePad() { return hudPoint2d; } +var gamePad = Controller.findDevice("GamePad"); function activeHudPoint2d(activeHand) { // if controller is valid, update reticle position and answer 2d point. Otherwise falsey. var controllerPose = getControllerWorldLocation(activeHand, true); // note: this will return head pose if hand pose is invalid (third eye) if (!controllerPose.valid) { @@ -377,7 +378,6 @@ var leftTrigger = new Trigger('left'); var rightTrigger = new Trigger('right'); var activeTrigger = rightTrigger; var activeHand = Controller.Standard.RightHand; -var gamePad = Controller.findDevice("GamePad"); var LEFT_HUD_LASER = 1; var RIGHT_HUD_LASER = 2; var BOTH_HUD_LASERS = LEFT_HUD_LASER + RIGHT_HUD_LASER; @@ -449,6 +449,8 @@ clickMapping.from(Controller.Standard.LeftSecondaryThumb).peek().to(function (cl wantsMenu = clicked; }); clickMapping.from(Controller.Hardware.GamePad.Back).peek().to(function () { + // Wait a tick an allow the reticle to be correctly set to the players look at position before invoking + // ContextMenu action Script.setTimeout(function () { activeHudPoint2dGamePad(); }, 0); @@ -505,16 +507,16 @@ function update() { expireMouseCursor(); clearSystemLaser(); } - //print("In updating HandControllerPointer"); + updateSeeking(true); if (!handControllerLockOut.expired(now)) { return off(); // Let them use mouse in peace. } - //print("test2"); + if (!Menu.isOptionChecked("First Person")) { return off(); // What to do? menus can be behind hand! } - //print("test3"); + if ((!Window.hasFocus() && !HMD.active) || !Reticle.allowMouseCapture) { // In desktop it's pretty clear when another app is on top. In that case we bail, because // hand controllers might be sputtering "valid" data and that will keep someone from deliberately From d29b1c34b1cc564a0670cbe2d6035f8aa962111b Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Wed, 2 Nov 2016 00:07:58 +0100 Subject: [PATCH 03/29] fixed coding standard conflicts --- scripts/system/controllers/handControllerPointer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/system/controllers/handControllerPointer.js b/scripts/system/controllers/handControllerPointer.js index 77cc7603e5..0ad297ccd3 100644 --- a/scripts/system/controllers/handControllerPointer.js +++ b/scripts/system/controllers/handControllerPointer.js @@ -231,7 +231,7 @@ var gamePad = Controller.findDevice("GamePad"); function activeHudPoint2d(activeHand) { // if controller is valid, update reticle position and answer 2d point. Otherwise falsey. var controllerPose = getControllerWorldLocation(activeHand, true); // note: this will return head pose if hand pose is invalid (third eye) if (!controllerPose.valid) { - if(gamePad){ + if (gamePad) { return activeHudPoint2dGamePad(); } return; // Controller is cradled. @@ -458,7 +458,7 @@ clickMapping.from(Controller.Hardware.GamePad.Back).peek().to(function () { clickMapping.from(Controller.Hardware.Keyboard.RightMouseClicked).peek().to(function () { // Allow the reticle depth to be set correctly: // Wait a tick for the context menu to be displayed, and then simulate a (non-hand-controller) mouse move - // so that the system updates qml state (Reticle.pointingAtSystemOverlay) before it gives us a mouseMove. + // so that the system updates qml state (Reticle.pointingAtSystemverlay) before it gives us a mouseMove. // We don't want the system code to always do this for us, because, e.g., we do not want to get a mouseMove // after the Left/RightSecondaryThumb gives us a context menu. Only from the mouse. Script.setTimeout(function () { From 1a83cc2c3e0dc2c4691319ec2d36c561d87a6cf7 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Wed, 2 Nov 2016 00:12:23 +0100 Subject: [PATCH 04/29] fixed deleted comment --- scripts/system/controllers/handControllerPointer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/controllers/handControllerPointer.js b/scripts/system/controllers/handControllerPointer.js index 0ad297ccd3..82344426cb 100644 --- a/scripts/system/controllers/handControllerPointer.js +++ b/scripts/system/controllers/handControllerPointer.js @@ -458,7 +458,7 @@ clickMapping.from(Controller.Hardware.GamePad.Back).peek().to(function () { clickMapping.from(Controller.Hardware.Keyboard.RightMouseClicked).peek().to(function () { // Allow the reticle depth to be set correctly: // Wait a tick for the context menu to be displayed, and then simulate a (non-hand-controller) mouse move - // so that the system updates qml state (Reticle.pointingAtSystemverlay) before it gives us a mouseMove. + // so that the system updates qml state (Reticle.pointingAtSystemOverlay) before it gives us a mouseMove. // We don't want the system code to always do this for us, because, e.g., we do not want to get a mouseMove // after the Left/RightSecondaryThumb gives us a context menu. Only from the mouse. Script.setTimeout(function () { From e22c36b4b7800eae88cf7b081be0d673f1931a7c Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Mon, 7 Nov 2016 17:16:24 +0000 Subject: [PATCH 05/29] fixed menu pop up delay --- interface/resources/controllers/xbox.json | 30 +++++++++---------- .../controllers/handControllerPointer.js | 18 +++++------ 2 files changed, 23 insertions(+), 25 deletions(-) diff --git a/interface/resources/controllers/xbox.json b/interface/resources/controllers/xbox.json index b0e97b849f..36065b87a1 100644 --- a/interface/resources/controllers/xbox.json +++ b/interface/resources/controllers/xbox.json @@ -4,12 +4,12 @@ { "from": "GamePad.LY", "filters": { "type": "deadZone", "min": 0.05 }, "to": "Actions.TranslateZ" }, { "from": "GamePad.LX", "filters": { "type": "deadZone", "min": 0.05 }, "to": "Actions.TranslateX" }, - { "from": "GamePad.LT", "to": "Standard.LTClick", + { "from": "GamePad.LT", "to": "Standard.LTClick", "peek": true, "filters": [ { "type": "hysteresis", "min": 0.85, "max": 0.9 } ] }, - { "from": "GamePad.LT", "to": "Standard.LT" }, - { "from": "GamePad.LB", "to": "Standard.LB" }, + { "from": "GamePad.LT", "to": "Standard.LT" }, + { "from": "GamePad.LB", "to": "Standard.LB" }, { "from": "GamePad.LS", "to": "Standard.LS" }, @@ -27,34 +27,34 @@ { "from": "GamePad.RX", "to": "Actions.Yaw" }, - { "from": "GamePad.RY", - "to": "Actions.VERTICAL_UP", - "filters": + { "from": "GamePad.RY", + "to": "Actions.VERTICAL_UP", + "filters": [ { "type": "deadZone", "min": 0.95 }, "invert" ] - }, + }, - { "from": "GamePad.RT", "to": "Standard.RTClick", + { "from": "GamePad.RT", "to": "Standard.RTClick", "peek": true, "filters": [ { "type": "hysteresis", "min": 0.85, "max": 0.9 } ] }, - { "from": "GamePad.RT", "to": "Standard.RT" }, - { "from": "GamePad.RB", "to": "Standard.RB" }, + { "from": "GamePad.RT", "to": "Standard.RT" }, + { "from": "GamePad.RB", "to": "Standard.RB" }, { "from": "GamePad.RS", "to": "Standard.RS" }, { "from": "GamePad.Start", "to": "Actions.CycleCamera" }, - { "from": "GamePad.Back", "to": "Actions.ContextMenu" }, + { "from": "GamePad.Back", "to": "Standard.Start" }, { "from": "GamePad.DU", "to": "Standard.DU" }, - { "from": "GamePad.DD", "to": "Standard.DD" }, + { "from": "GamePad.DD", "to": "Standard.DD" }, { "from": "GamePad.DL", "to": "Standard.DL" }, - { "from": "GamePad.DR", "to": "Standard.DR" }, + { "from": "GamePad.DR", "to": "Standard.DR" }, { "from": [ "GamePad.Y" ], "to": "Standard.RightPrimaryThumb", "peek": true }, - { "from": "GamePad.A", "to": "Standard.A" }, - { "from": "GamePad.B", "to": "Standard.B" }, + { "from": "GamePad.A", "to": "Standard.A" }, + { "from": "GamePad.B", "to": "Standard.B" }, { "from": "GamePad.X", "to": "Standard.X" }, { "from": "GamePad.Y", "to": "Standard.Y" } ] diff --git a/scripts/system/controllers/handControllerPointer.js b/scripts/system/controllers/handControllerPointer.js index 82344426cb..ce9b8e403f 100644 --- a/scripts/system/controllers/handControllerPointer.js +++ b/scripts/system/controllers/handControllerPointer.js @@ -203,6 +203,7 @@ function overlayFromWorldPoint(point) { return { x: horizontalPixels, y: verticalPixels }; } +var gamePad = Controller.findDevice("GamePad"); function activeHudPoint2dGamePad() { if (!HMD.active) { return; @@ -227,13 +228,10 @@ function activeHudPoint2dGamePad() { return hudPoint2d; } -var gamePad = Controller.findDevice("GamePad"); + function activeHudPoint2d(activeHand) { // if controller is valid, update reticle position and answer 2d point. Otherwise falsey. var controllerPose = getControllerWorldLocation(activeHand, true); // note: this will return head pose if hand pose is invalid (third eye) if (!controllerPose.valid) { - if (gamePad) { - return activeHudPoint2dGamePad(); - } return; // Controller is cradled. } var controllerPosition = controllerPose.position; @@ -448,12 +446,12 @@ clickMapping.from(Controller.Standard.LeftSecondaryThumb).peek().to(function (cl } wantsMenu = clicked; }); -clickMapping.from(Controller.Hardware.GamePad.Back).peek().to(function () { - // Wait a tick an allow the reticle to be correctly set to the players look at position before invoking - // ContextMenu action - Script.setTimeout(function () { - activeHudPoint2dGamePad(); - }, 0); +clickMapping.from(Controller.Standard.Start).peek().to(function (clicked) { + if (clicked) { + activeHudPoint2dGamePad(); + } + + wantsMenu = clicked; }); clickMapping.from(Controller.Hardware.Keyboard.RightMouseClicked).peek().to(function () { // Allow the reticle depth to be set correctly: From 86a569651c501da4f4f3e7d70e809b8023b67135 Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 7 Nov 2016 14:45:26 -0800 Subject: [PATCH 06/29] Add more precuation when extracting the opengl version in the openGLVersionChecker to avoid crashing --- libraries/gl/src/gl/OpenGLVersionChecker.cpp | 25 ++++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/libraries/gl/src/gl/OpenGLVersionChecker.cpp b/libraries/gl/src/gl/OpenGLVersionChecker.cpp index 6473b6bf2b..9a41ebfdca 100644 --- a/libraries/gl/src/gl/OpenGLVersionChecker.cpp +++ b/libraries/gl/src/gl/OpenGLVersionChecker.cpp @@ -75,15 +75,26 @@ QJsonObject OpenGLVersionChecker::checkVersion(bool& valid, bool& override) { // - major_number.minor_number // - major_number.minor_number.release_number // Reference: https://www.opengl.org/sdk/docs/man/docbook4/xhtml/glGetString.xml - const QString version { "version" }; - QString glVersion = glData[version].toString(); - QStringList versionParts = glVersion.split(QRegularExpression("[\\.\\s]")); - int majorNumber = versionParts[0].toInt(); - int minorNumber = versionParts[1].toInt(); + int minimumMajorNumber = (MINIMUM_GL_VERSION >> 16); int minimumMinorNumber = (MINIMUM_GL_VERSION & 0xFF); - valid = (majorNumber > minimumMajorNumber - || (majorNumber == minimumMajorNumber && minorNumber >= minimumMinorNumber)); + int majorNumber = 0; + int minorNumber = 0; + const QString version { "version" }; + if (version.contains(version)) { + QString glVersion = glData[version].toString(); + QStringList versionParts = glVersion.split(QRegularExpression("[\\.\\s]")); + if (versionParts.size() >= 2) { + majorNumber = versionParts[0].toInt(); + minorNumber = versionParts[1].toInt(); + valid = (majorNumber > minimumMajorNumber + || (majorNumber == minimumMajorNumber && minorNumber >= minimumMinorNumber)); + } else { + valid = false; + } + } else { + valid = false; + } // Prompt user if below minimum if (!valid) { From 284b458148dd9e0a1f32700bab7ee211d16301f6 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 8 Nov 2016 09:46:47 -0800 Subject: [PATCH 07/29] Increase margin at bottom of users.js --- scripts/system/users.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/scripts/system/users.js b/scripts/system/users.js index 1e4cf042f3..68501721b3 100644 --- a/scripts/system/users.js +++ b/scripts/system/users.js @@ -228,7 +228,7 @@ var usersWindow = (function () { var WINDOW_WIDTH = 260, WINDOW_MARGIN = 12, - WINDOW_BASE_MARGIN = 6, // A little less is needed in order look correct + WINDOW_BASE_MARGIN = 24, // A little less is needed in order look correct WINDOW_FONT = { size: 12 }, @@ -254,10 +254,11 @@ var usersWindow = (function () { windowHeading, // Window border is similar to that of edit.js. - WINDOW_BORDER_WIDTH = WINDOW_WIDTH + 2 * WINDOW_BASE_MARGIN, - WINDOW_BORDER_TOP_MARGIN = 2 * WINDOW_BASE_MARGIN, - WINDOW_BORDER_BOTTOM_MARGIN = WINDOW_BASE_MARGIN, - WINDOW_BORDER_LEFT_MARGIN = WINDOW_BASE_MARGIN, + WINDOW_MARGIN_HALF = WINDOW_MARGIN / 2, + WINDOW_BORDER_WIDTH = WINDOW_WIDTH + 2 * WINDOW_MARGIN_HALF, + WINDOW_BORDER_TOP_MARGIN = 2 * WINDOW_MARGIN_HALF, + WINDOW_BORDER_BOTTOM_MARGIN = WINDOW_MARGIN_HALF, + WINDOW_BORDER_LEFT_MARGIN = WINDOW_MARGIN_HALF, WINDOW_BORDER_RADIUS = 4, WINDOW_BORDER_COLOR = { red: 255, green: 255, blue: 255 }, WINDOW_BORDER_ALPHA = 0.5, From 5dab2ac2a7b492b46d741f818bfdb3ccd4aaf412 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 8 Nov 2016 09:47:09 -0800 Subject: [PATCH 08/29] Add margin to left and right side of users.js --- scripts/system/users.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/scripts/system/users.js b/scripts/system/users.js index 68501721b3..d03a5bb846 100644 --- a/scripts/system/users.js +++ b/scripts/system/users.js @@ -253,6 +253,11 @@ var usersWindow = (function () { windowPane, windowHeading, + // Margin on the left and right side of the window to keep + // it from getting too close to the edge of the screen which + // is unclickable. + WINDOW_MARGIN_X = 20, + // Window border is similar to that of edit.js. WINDOW_MARGIN_HALF = WINDOW_MARGIN / 2, WINDOW_BORDER_WIDTH = WINDOW_WIDTH + 2 * WINDOW_MARGIN_HALF, @@ -446,7 +451,7 @@ var usersWindow = (function () { } function saturateWindowPosition() { - windowPosition.x = Math.max(0, Math.min(viewport.x - WINDOW_WIDTH, windowPosition.x)); + windowPosition.x = Math.max(WINDOW_MARGIN_X, Math.min(viewport.x - WINDOW_WIDTH - WINDOW_MARGIN_X, windowPosition.x)); windowPosition.y = Math.max(windowMinimumHeight, Math.min(viewport.y, windowPosition.y)); } From a4e75b4dcf334bcbf9980d187a2cfabbaa730970 Mon Sep 17 00:00:00 2001 From: samcake Date: Tue, 8 Nov 2016 14:51:48 -0800 Subject: [PATCH 09/29] FIx an obvious mistake, good catch MR Zappoman --- libraries/gl/src/gl/OpenGLVersionChecker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/gl/src/gl/OpenGLVersionChecker.cpp b/libraries/gl/src/gl/OpenGLVersionChecker.cpp index 9a41ebfdca..24b61e5128 100644 --- a/libraries/gl/src/gl/OpenGLVersionChecker.cpp +++ b/libraries/gl/src/gl/OpenGLVersionChecker.cpp @@ -81,7 +81,7 @@ QJsonObject OpenGLVersionChecker::checkVersion(bool& valid, bool& override) { int majorNumber = 0; int minorNumber = 0; const QString version { "version" }; - if (version.contains(version)) { + if (glData.contains(version)) { QString glVersion = glData[version].toString(); QStringList versionParts = glVersion.split(QRegularExpression("[\\.\\s]")); if (versionParts.size() >= 2) { From b7381c30cb605ed7e23aa3f7c8d0d773905c5f25 Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 9 Nov 2016 10:38:44 -0800 Subject: [PATCH 10/29] Including the fix for the bad Major Version number from David K --- libraries/gl/src/gl/OpenGLVersionChecker.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/gl/src/gl/OpenGLVersionChecker.cpp b/libraries/gl/src/gl/OpenGLVersionChecker.cpp index 24b61e5128..428bf86c6f 100644 --- a/libraries/gl/src/gl/OpenGLVersionChecker.cpp +++ b/libraries/gl/src/gl/OpenGLVersionChecker.cpp @@ -21,6 +21,7 @@ #include "GLHelpers.h" +// Minimum gl version required is 4.1 #define MINIMUM_GL_VERSION 0x0401 OpenGLVersionChecker::OpenGLVersionChecker(int& argc, char** argv) : @@ -76,7 +77,7 @@ QJsonObject OpenGLVersionChecker::checkVersion(bool& valid, bool& override) { // - major_number.minor_number.release_number // Reference: https://www.opengl.org/sdk/docs/man/docbook4/xhtml/glGetString.xml - int minimumMajorNumber = (MINIMUM_GL_VERSION >> 16); + int minimumMajorNumber = (MINIMUM_GL_VERSION >> 8) & 0xFF; int minimumMinorNumber = (MINIMUM_GL_VERSION & 0xFF); int majorNumber = 0; int minorNumber = 0; From 941e5a57b18c532b683ccd66d729727688d63290 Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 9 Nov 2016 17:25:46 -0800 Subject: [PATCH 11/29] trying a test with 4.4 as the minimum gl context to be able to break on Intel integrated --- libraries/gl/src/gl/Context.cpp | 6 ++++-- libraries/gl/src/gl/OpenGLVersionChecker.cpp | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/libraries/gl/src/gl/Context.cpp b/libraries/gl/src/gl/Context.cpp index ec1c284e99..fe48968981 100644 --- a/libraries/gl/src/gl/Context.cpp +++ b/libraries/gl/src/gl/Context.cpp @@ -217,8 +217,10 @@ void Context::create() { glewInit(); if (glewIsSupported("GL_VERSION_4_5")) { _version = 0x0405; - } else if (glewIsSupported("GL_VERSION_4_3")) { - _version = 0x0403; + // } else if (glewIsSupported("GL_VERSION_4_3")) { + } else if (glewIsSupported("GL_VERSION_4_4")) { +///_version = 0x0403; + _version = 0x0404; } glGetError(); wglMakeCurrent(0, 0); diff --git a/libraries/gl/src/gl/OpenGLVersionChecker.cpp b/libraries/gl/src/gl/OpenGLVersionChecker.cpp index 428bf86c6f..273251581a 100644 --- a/libraries/gl/src/gl/OpenGLVersionChecker.cpp +++ b/libraries/gl/src/gl/OpenGLVersionChecker.cpp @@ -22,7 +22,8 @@ #include "GLHelpers.h" // Minimum gl version required is 4.1 -#define MINIMUM_GL_VERSION 0x0401 +//#define MINIMUM_GL_VERSION 0x0401 +#define MINIMUM_GL_VERSION 0x0404 OpenGLVersionChecker::OpenGLVersionChecker(int& argc, char** argv) : QApplication(argc, argv) From c01b39b54bdd0fe83fdfb8690a640ae98f1d2cef Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 9 Nov 2016 18:16:51 -0800 Subject: [PATCH 12/29] And test that again --- libraries/gl/src/gl/Context.cpp | 6 +++--- libraries/gl/src/gl/OpenGLVersionChecker.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/gl/src/gl/Context.cpp b/libraries/gl/src/gl/Context.cpp index fe48968981..221223d584 100644 --- a/libraries/gl/src/gl/Context.cpp +++ b/libraries/gl/src/gl/Context.cpp @@ -215,10 +215,10 @@ void Context::create() { } glewExperimental = true; glewInit(); - if (glewIsSupported("GL_VERSION_4_5")) { - _version = 0x0405; + // if (glewIsSupported("GL_VERSION_4_5")) { + // _version = 0x0405; // } else if (glewIsSupported("GL_VERSION_4_3")) { - } else if (glewIsSupported("GL_VERSION_4_4")) { + /* } else*/ if (glewIsSupported("GL_VERSION_4_8")) { ///_version = 0x0403; _version = 0x0404; } diff --git a/libraries/gl/src/gl/OpenGLVersionChecker.cpp b/libraries/gl/src/gl/OpenGLVersionChecker.cpp index 273251581a..322a7bc484 100644 --- a/libraries/gl/src/gl/OpenGLVersionChecker.cpp +++ b/libraries/gl/src/gl/OpenGLVersionChecker.cpp @@ -23,7 +23,7 @@ // Minimum gl version required is 4.1 //#define MINIMUM_GL_VERSION 0x0401 -#define MINIMUM_GL_VERSION 0x0404 +#define MINIMUM_GL_VERSION 0x0408 OpenGLVersionChecker::OpenGLVersionChecker(int& argc, char** argv) : QApplication(argc, argv) From 0ab4a781f59513f598de3e4eaafb38a5a1e1c412 Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 9 Nov 2016 18:25:37 -0800 Subject: [PATCH 13/29] Back to the correct code --- libraries/gl/src/gl/Context.cpp | 10 ++++------ libraries/gl/src/gl/OpenGLVersionChecker.cpp | 3 +-- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/libraries/gl/src/gl/Context.cpp b/libraries/gl/src/gl/Context.cpp index 221223d584..ec1c284e99 100644 --- a/libraries/gl/src/gl/Context.cpp +++ b/libraries/gl/src/gl/Context.cpp @@ -215,12 +215,10 @@ void Context::create() { } glewExperimental = true; glewInit(); - // if (glewIsSupported("GL_VERSION_4_5")) { - // _version = 0x0405; - // } else if (glewIsSupported("GL_VERSION_4_3")) { - /* } else*/ if (glewIsSupported("GL_VERSION_4_8")) { -///_version = 0x0403; - _version = 0x0404; + if (glewIsSupported("GL_VERSION_4_5")) { + _version = 0x0405; + } else if (glewIsSupported("GL_VERSION_4_3")) { + _version = 0x0403; } glGetError(); wglMakeCurrent(0, 0); diff --git a/libraries/gl/src/gl/OpenGLVersionChecker.cpp b/libraries/gl/src/gl/OpenGLVersionChecker.cpp index 322a7bc484..428bf86c6f 100644 --- a/libraries/gl/src/gl/OpenGLVersionChecker.cpp +++ b/libraries/gl/src/gl/OpenGLVersionChecker.cpp @@ -22,8 +22,7 @@ #include "GLHelpers.h" // Minimum gl version required is 4.1 -//#define MINIMUM_GL_VERSION 0x0401 -#define MINIMUM_GL_VERSION 0x0408 +#define MINIMUM_GL_VERSION 0x0401 OpenGLVersionChecker::OpenGLVersionChecker(int& argc, char** argv) : QApplication(argc, argv) From db98a742b60f5075ec848c206a4e4dc00ae7b26b Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 9 Nov 2016 12:22:57 -0800 Subject: [PATCH 14/29] Remove incremental transfers --- interface/src/Menu.cpp | 9 --- interface/src/Menu.h | 1 - libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 8 ++- .../src/gpu/gl45/GL45BackendTexture.cpp | 67 +++++++++++-------- libraries/gpu/src/gpu/Texture.cpp | 23 +------ libraries/gpu/src/gpu/Texture.h | 7 +- 6 files changed, 51 insertions(+), 64 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 6207ecdb3c..dfa1a2f8b9 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -396,15 +396,6 @@ Menu::Menu() { }); } - // Developer > Render > Enable Incremental Texture Transfer - { - auto action = addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::IncrementalTextureTransfer, 0, gpu::Texture::getEnableIncrementalTextureTransfers()); - connect(action, &QAction::triggered, [&](bool checked) { - qDebug() << "[TEXTURE TRANSFER SUPPORT] --- Enable Incremental Texture Transfer menu option:" << checked; - gpu::Texture::setEnableIncrementalTextureTransfers(checked); - }); - } - #else qDebug() << "[TEXTURE TRANSFER SUPPORT] Incremental Texture Transfer and Dynamic Texture Management not supported on this platform."; #endif diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 87e0e678f1..e339da4d25 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -112,7 +112,6 @@ namespace MenuOption { const QString FrameTimer = "Show Timer"; const QString FullscreenMirror = "Mirror"; const QString Help = "Help..."; - const QString IncrementalTextureTransfer = "Enable Incremental Texture Transfer"; const QString IncreaseAvatarSize = "Increase Avatar Size"; const QString IndependentMode = "Independent Mode"; const QString ActionMotorControl = "Enable Default Motor Control"; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index 438b1e9454..291888e81b 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -14,6 +14,8 @@ #include "../gl/GLBackend.h" #include "../gl/GLTexture.h" +#define INCREMENTAL_TRANSFER 0 + namespace gpu { namespace gl45 { using namespace gpu::gl; @@ -56,6 +58,7 @@ public: GLint pageDimensionsIndex { 0 }; }; +#if INCREMENTAL_TRANSFER struct TransferState { TransferState(GL45Texture& texture); uvec3 currentPageSize() const; @@ -74,6 +77,10 @@ public: uvec3 mipOffset; const uint8_t* srcPointer { nullptr }; }; + protected: + TransferState _transferState; +#endif + protected: void updateMips() override; void stripToMip(uint16_t newMinMip); @@ -91,7 +98,6 @@ public: void derez(); SparseInfo _sparseInfo; - TransferState _transferState; uint32_t _allocatedPages { 0 }; uint32_t _lastMipAllocatedPages { 0 }; uint16_t _mipOffset { 0 }; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index f97364f5c5..fca809186f 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -162,6 +162,8 @@ void GL45Backend::initTextureManagementStage() { } } +#if INCREMENTAL_TRANSFER + using TransferState = GL45Backend::GL45Texture::TransferState; TransferState::TransferState(GL45Texture& texture) : texture(texture) { @@ -246,6 +248,7 @@ void TransferState::populatePage(std::vector& buffer) { uvec3 TransferState::currentPageSize() const { return glm::clamp(mipDimensions - mipOffset, uvec3(1), texture._sparseInfo.pageDimensions); } +#endif GLuint GL45Texture::allocate(const Texture& texture) { GLuint result; @@ -258,11 +261,19 @@ GLuint GL45Backend::getTextureID(const TexturePointer& texture, bool transfer) { } GL45Texture::GL45Texture(const std::weak_ptr& backend, const Texture& texture, GLuint externalId) - : GLTexture(backend, texture, externalId), _sparseInfo(*this), _transferState(*this) { + : GLTexture(backend, texture, externalId), _sparseInfo(*this) +#if INCREMENTAL_TRANSFER +, _transferState(*this) +#endif +{ } GL45Texture::GL45Texture(const std::weak_ptr& backend, const Texture& texture, bool transferrable) - : GLTexture(backend, texture, allocate(texture), transferrable), _sparseInfo(*this), _transferState(*this) { + : GLTexture(backend, texture, allocate(texture), transferrable), _sparseInfo(*this) +#if INCREMENTAL_TRANSFER +, _transferState(*this) +#endif + { auto theBackend = _backend.lock(); if (_transferrable && theBackend && theBackend->isTextureManagementSparseEnabled()) { @@ -375,39 +386,40 @@ void GL45Texture::updateSize() const { void GL45Texture::startTransfer() { Parent::startTransfer(); _sparseInfo.update(); +#if INCREMENTAL_TRANSFER _transferState.updateMip(); +#endif } bool GL45Texture::continueTransfer() { - if (!Texture::getEnableIncrementalTextureTransfers()) { - size_t maxFace = GL_TEXTURE_CUBE_MAP == _target ? CUBE_NUM_FACES : 1; - for (uint8_t face = 0; face < maxFace; ++face) { - for (uint16_t mipLevel = _minMip; mipLevel <= _maxMip; ++mipLevel) { - auto size = _gpuObject.evalMipDimensions(mipLevel); - if (_sparseInfo.sparse && mipLevel <= _sparseInfo.maxSparseLevel) { - glTexturePageCommitmentEXT(_id, mipLevel, 0, 0, face, size.x, size.y, 1, GL_TRUE); - _allocatedPages += _sparseInfo.getPageCount(size); - } - if (_gpuObject.isStoredMipFaceAvailable(mipLevel, face)) { - auto mip = _gpuObject.accessStoredMipFace(mipLevel, face); - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), mip->getFormat()); - if (GL_TEXTURE_2D == _target) { - glTextureSubImage2D(_id, mipLevel, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mip->readData()); - } else if (GL_TEXTURE_CUBE_MAP == _target) { - // DSA ARB does not work on AMD, so use EXT - // glTextureSubImage3D(_id, mipLevel, 0, 0, face, size.x, size.y, 1, texelFormat.format, texelFormat.type, mip->readData()); - auto target = CUBE_FACE_LAYOUT[face]; - glTextureSubImage2DEXT(_id, target, mipLevel, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mip->readData()); - } else { - Q_ASSERT(false); - } - (void)CHECK_GL_ERROR(); +#if !INCREMENTAL_TRANSFER + size_t maxFace = GL_TEXTURE_CUBE_MAP == _target ? CUBE_NUM_FACES : 1; + for (uint8_t face = 0; face < maxFace; ++face) { + for (uint16_t mipLevel = _minMip; mipLevel <= _maxMip; ++mipLevel) { + auto size = _gpuObject.evalMipDimensions(mipLevel); + if (_sparseInfo.sparse && mipLevel <= _sparseInfo.maxSparseLevel) { + glTexturePageCommitmentEXT(_id, mipLevel, 0, 0, face, size.x, size.y, 1, GL_TRUE); + _allocatedPages += _sparseInfo.getPageCount(size); + } + if (_gpuObject.isStoredMipFaceAvailable(mipLevel, face)) { + auto mip = _gpuObject.accessStoredMipFace(mipLevel, face); + GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), mip->getFormat()); + if (GL_TEXTURE_2D == _target) { + glTextureSubImage2D(_id, mipLevel, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mip->readData()); + } else if (GL_TEXTURE_CUBE_MAP == _target) { + // DSA ARB does not work on AMD, so use EXT + // glTextureSubImage3D(_id, mipLevel, 0, 0, face, size.x, size.y, 1, texelFormat.format, texelFormat.type, mip->readData()); + auto target = CUBE_FACE_LAYOUT[face]; + glTextureSubImage2DEXT(_id, target, mipLevel, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mip->readData()); + } else { + Q_ASSERT(false); } + (void)CHECK_GL_ERROR(); } } - return false; } - + return false; +#else static std::vector buffer; if (buffer.empty()) { buffer.resize(DEFAULT_PAGE_BUFFER_SIZE); @@ -458,6 +470,7 @@ bool GL45Texture::continueTransfer() { _lastMipAllocatedPages = _allocatedPages; } return result; +#endif } void GL45Texture::finishTransfer() { diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 33786155db..45aff54b8f 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -35,18 +35,15 @@ std::atomic Texture::_allowedCPUMemoryUsage { 0 }; #define MIN_CORES_FOR_INCREMENTAL_TEXTURES 5 -bool recommendedIncrementalTransfers = (QThread::idealThreadCount() >= MIN_CORES_FOR_INCREMENTAL_TEXTURES); -bool recommendedSparseTextures = recommendedIncrementalTransfers; +bool recommendedSparseTextures = (QThread::idealThreadCount() >= MIN_CORES_FOR_INCREMENTAL_TEXTURES); -std::atomic Texture::_enableSparseTextures { recommendedIncrementalTransfers }; -std::atomic Texture::_enableIncrementalTextureTransfers { recommendedSparseTextures }; +std::atomic Texture::_enableSparseTextures { recommendedSparseTextures }; struct ReportTextureState { ReportTextureState() { qDebug() << "[TEXTURE TRANSFER SUPPORT]" << "\n\tidealThreadCount:" << QThread::idealThreadCount() - << "\n\tRECOMMENDED enableSparseTextures:" << recommendedSparseTextures - << "\n\tRECOMMENDED enableIncrementalTextures:" << recommendedIncrementalTransfers; + << "\n\tRECOMMENDED enableSparseTextures:" << recommendedSparseTextures; } } report; @@ -59,16 +56,6 @@ void Texture::setEnableSparseTextures(bool enabled) { #endif } -void Texture::setEnableIncrementalTextureTransfers(bool enabled) { -#ifdef Q_OS_WIN - qDebug() << "[TEXTURE TRANSFER SUPPORT] SETTING - Enable Incremental Texture Transfer:" << enabled; - _enableIncrementalTextureTransfers = enabled; -#else - qDebug() << "[TEXTURE TRANSFER SUPPORT] Incremental Texture Transfer not supported on this platform."; -#endif -} - - void Texture::updateTextureCPUMemoryUsage(Size prevObjectSize, Size newObjectSize) { if (prevObjectSize == newObjectSize) { return; @@ -84,10 +71,6 @@ bool Texture::getEnableSparseTextures() { return _enableSparseTextures.load(); } -bool Texture::getEnableIncrementalTextureTransfers() { - return _enableIncrementalTextureTransfers.load(); -} - uint32_t Texture::getTextureCPUCount() { return _textureCPUCount.load(); } diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 2a93ec3066..856bd4983d 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -143,10 +143,8 @@ class Texture : public Resource { static std::atomic _textureCPUCount; static std::atomic _textureCPUMemoryUsage; static std::atomic _allowedCPUMemoryUsage; - static void updateTextureCPUMemoryUsage(Size prevObjectSize, Size newObjectSize); - static std::atomic _enableSparseTextures; - static std::atomic _enableIncrementalTextureTransfers; + static void updateTextureCPUMemoryUsage(Size prevObjectSize, Size newObjectSize); public: static uint32_t getTextureCPUCount(); @@ -162,10 +160,7 @@ public: static void setAllowedGPUMemoryUsage(Size size); static bool getEnableSparseTextures(); - static bool getEnableIncrementalTextureTransfers(); - static void setEnableSparseTextures(bool enabled); - static void setEnableIncrementalTextureTransfers(bool enabled); using ExternalRecycler = std::function; using ExternalIdAndFence = std::pair; From 563322d08d2aeaea69c9ee886876230ace733fa9 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 10 Nov 2016 10:02:31 -0800 Subject: [PATCH 15/29] add min and max avatar scale to domain-server settings --- .../resources/describe-settings.json | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index e6663888b4..6c4ab9fd77 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -866,6 +866,28 @@ } ] }, + { + "name": "avatars", + "label": "Avatars", + "settings": [ + { + "name": "min_avatar_scale", + "type": "double", + "label": "Minimum Avatar Scale (meters)", + "help": "Limits the scale of avatars in your domain", + "placeholder": 0.01, + "default": 0.01 + }, + { + "name": "max_avatar_scale", + "type": "double", + "label": "Maximum Avatar Scale (meters)", + "help": "Limits the scale of avatars in your domain", + "placeholder": 4.0, + "default": 4.0 + } + ] + }, { "name": "audio_env", "label": "Audio Environment", From e2991c366529022c1a5fee098caeaf74db240b50 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 10 Nov 2016 10:35:05 -0800 Subject: [PATCH 16/29] Change minimum cores to 3 to enable OpenVR --- plugins/openvr/src/OpenVrHelpers.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/openvr/src/OpenVrHelpers.cpp b/plugins/openvr/src/OpenVrHelpers.cpp index 2803ca424e..0c3a2faed6 100644 --- a/plugins/openvr/src/OpenVrHelpers.cpp +++ b/plugins/openvr/src/OpenVrHelpers.cpp @@ -331,7 +331,7 @@ controller::Pose openVrControllerPoseToHandPose(bool isLeftHand, const mat4& mat #define FAILED_MIN_SPEC_OVERLAY_FRIENDLY_NAME "Minimum specifications for SteamVR not met" #define FAILED_MIN_SPEC_UPDATE_INTERVAL_MS 10 #define FAILED_MIN_SPEC_AUTO_QUIT_INTERVAL_MS (MSECS_PER_SECOND * 30) -#define MIN_CORES_SPEC 5 +#define MIN_CORES_SPEC 3 void showMinSpecWarning() { auto vrSystem = acquireOpenVrSystem(); From 77ede81fc906adb51abe68a9ef77c37a36a60520 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 10 Nov 2016 13:27:13 -0800 Subject: [PATCH 17/29] limit MyAvatar scale to limits from domain settings --- .../src/avatars/ScriptableAvatar.h | 4 +- .../resources/describe-settings.json | 12 +-- interface/src/avatar/MyAvatar.cpp | 78 +++++++++++++++++-- interface/src/avatar/MyAvatar.h | 3 + libraries/avatars/src/AvatarData.cpp | 8 +- libraries/avatars/src/AvatarData.h | 6 ++ 6 files changed, 92 insertions(+), 19 deletions(-) diff --git a/assignment-client/src/avatars/ScriptableAvatar.h b/assignment-client/src/avatars/ScriptableAvatar.h index 56707de471..18d64f4ac5 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.h +++ b/assignment-client/src/avatars/ScriptableAvatar.h @@ -17,7 +17,7 @@ #include #include -class ScriptableAvatar : public AvatarData, public Dependency{ +class ScriptableAvatar : public AvatarData, public Dependency { Q_OBJECT public: @@ -39,4 +39,4 @@ private: std::shared_ptr _animSkeleton; }; -#endif // hifi_ScriptableAvatar_h \ No newline at end of file +#endif // hifi_ScriptableAvatar_h diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 6c4ab9fd77..b5c612881f 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -874,17 +874,17 @@ "name": "min_avatar_scale", "type": "double", "label": "Minimum Avatar Scale (meters)", - "help": "Limits the scale of avatars in your domain", - "placeholder": 0.01, - "default": 0.01 + "help": "Limits the scale of avatars in your domain. Must be at least than 0.005.", + "placeholder": 0.25, + "default": 0.25 }, { "name": "max_avatar_scale", "type": "double", "label": "Maximum Avatar Scale (meters)", - "help": "Limits the scale of avatars in your domain", - "placeholder": 4.0, - "default": 4.0 + "help": "Limits the scale of avatars in your domain. Cannot be greater than 1000.", + "placeholder": 3.0, + "default": 3.0 } ] }, diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 735ba05810..e1f0bd3a68 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -130,6 +130,15 @@ MyAvatar::MyAvatar(RigPointer rig) : connect(DependencyManager::get().data(), &AddressManager::locationChangeRequired, this, static_cast(&MyAvatar::goToLocation)); + // handle scale constraints imposed on us by the domain-server + auto& domainHandler = DependencyManager::get()->getDomainHandler(); + + // when we connect to a domain and retrieve its settings, we restrict our max/min scale based on those settings + connect(&domainHandler, &DomainHandler::settingsReceived, this, &MyAvatar::restrictScaleFromDomainSettings); + + // when we leave a domain we lift whatever restrictions that domain may have placed on our scale + connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, &MyAvatar::clearScaleRestriction); + _characterController.setEnabled(true); _bodySensorMatrix = deriveBodyFromHMDSensor(); @@ -1824,24 +1833,79 @@ bool findAvatarAvatarPenetration(const glm::vec3 positionA, float radiusA, float } void MyAvatar::increaseSize() { - if ((1.0f + SCALING_RATIO) * _targetScale < MAX_AVATAR_SCALE) { - _targetScale *= (1.0f + SCALING_RATIO); - qCDebug(interfaceapp, "Changed scale to %f", (double)_targetScale); + // clamp the target scale to the maximum allowable scale in the domain + + float updatedTargetScale = _targetScale * (1.0f + SCALING_RATIO); + + if (updatedTargetScale > _domainMaximumScale) { + qCDebug(interfaceapp, "Forced scale to %f since %f would be larger than allowed maximum", + _domainMaximumScale, updatedTargetScale); + + updatedTargetScale = _domainMaximumScale; } + + setTargetScale(updatedTargetScale); + qCDebug(interfaceapp, "Changed scale to %f", (double)_targetScale); } void MyAvatar::decreaseSize() { - if (MIN_AVATAR_SCALE < (1.0f - SCALING_RATIO) * _targetScale) { - _targetScale *= (1.0f - SCALING_RATIO); - qCDebug(interfaceapp, "Changed scale to %f", (double)_targetScale); + // clamp the target scale to the minimum allowable scale in the domain + float updatedTargetScale = _targetScale * (1.0f - SCALING_RATIO); + + if (updatedTargetScale < _domainMinimumScale) { + qCDebug(interfaceapp, "Forced scale to %f since %f would be smaller than allowed minimum", + _domainMinimumScale, updatedTargetScale); + + updatedTargetScale = _domainMinimumScale; } + + + setTargetScale(updatedTargetScale); + qCDebug(interfaceapp, "Changed scale to %f", (double)_targetScale); } void MyAvatar::resetSize() { - _targetScale = 1.0f; + // if the default + const float DEFAULT_AVATAR_SCALE = 1.0f; + + float allowedDefaultScale = glm::clamp(DEFAULT_AVATAR_SCALE, _domainMinimumScale, _domainMaximumScale); + + if (allowedDefaultScale != DEFAULT_AVATAR_SCALE) { + qCDebug(interfaceapp, "Forcing scale to %f since %f is not an allowed avatar scale by the domain", + allowedDefaultScale, DEFAULT_AVATAR_SCALE); + } + + setTargetScale(allowedDefaultScale); qCDebug(interfaceapp, "Reset scale to %f", (double)_targetScale); } +void MyAvatar::restrictScaleFromDomainSettings(const QJsonObject& domainSettingsObject) { + // pull out the minimum and maximum scale and set them to restrict our scale + + static const QString AVATAR_SETTINGS_KEY = "avatars"; + auto avatarsObject = domainSettingsObject[AVATAR_SETTINGS_KEY].toObject(); + + static const QString MIN_SCALE_OPTION = "min_avatar_scale"; + float settingMinScale = avatarsObject[MIN_SCALE_OPTION].toDouble(MIN_AVATAR_SCALE); + setDomainMinimumScale(settingMinScale); + + static const QString MAX_SCALE_OPTION = "max_avatar_scale"; + float settingMaxScale = avatarsObject[MAX_SCALE_OPTION].toDouble(MAX_AVATAR_SCALE); + setDomainMaximumScale(settingMaxScale); + + // debug to log if this avatar's scale in this domain will be clamped + auto clampedScale = glm::clamp(_targetScale, _domainMinimumScale, _domainMaximumScale); + + if (_targetScale != clampedScale) { + qCDebug(interfaceapp, "Avatar scale will be clamped to %f because %f is not allowed by current domain", + clampedScale, _targetScale); + } +} + +void MyAvatar::clearScaleRestriction() { + _domainMinimumScale = MIN_AVATAR_SCALE; + _domainMaximumScale = MAX_AVATAR_SCALE; +} void MyAvatar::goToLocation(const QVariant& propertiesVar) { qCDebug(interfaceapp, "MyAvatar QML goToLocation"); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 71f185c6ed..d9c88621d2 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -292,6 +292,9 @@ public slots: bool shouldFaceLocation = false); void goToLocation(const QVariant& properties); + void restrictScaleFromDomainSettings(const QJsonObject& domainSettingsObject); + void clearScaleRestriction(); + // Set/Get update the thrust that will move the avatar around void addThrust(glm::vec3 newThrust) { _thrust += newThrust; }; glm::vec3 getThrust() { return _thrust; }; diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 9df23dad2c..2469d0df04 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -210,7 +210,7 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) { packFloatAngleToTwoByte((uint8_t*)(header->localOrientation + 0), bodyEulerAngles.y); packFloatAngleToTwoByte((uint8_t*)(header->localOrientation + 1), bodyEulerAngles.x); packFloatAngleToTwoByte((uint8_t*)(header->localOrientation + 2), bodyEulerAngles.z); - packFloatRatioToTwoByte((uint8_t*)(&header->scale), _targetScale); + packFloatRatioToTwoByte((uint8_t*)(&header->scale), getDomainLimitedScale()); header->lookAtPosition[0] = _headData->_lookAtPosition.x; header->lookAtPosition[1] = _headData->_lookAtPosition.y; header->lookAtPosition[2] = _headData->_lookAtPosition.z; @@ -516,7 +516,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { } return buffer.size(); } - _targetScale = std::max(MIN_AVATAR_SCALE, std::min(MAX_AVATAR_SCALE, scale)); + setTargetScale(scale); glm::vec3 lookAt = glm::vec3(header->lookAtPosition[0], header->lookAtPosition[1], header->lookAtPosition[2]); if (isNaN(lookAt)) { @@ -1439,7 +1439,7 @@ QJsonObject AvatarData::toJson() const { if (!success) { qDebug() << "Warning -- AvatarData::toJson couldn't get avatar transform"; } - avatarTransform.setScale(getTargetScale()); + avatarTransform.setScale(getDomainLimitedScale()); if (recordingBasis) { root[JSON_AVATAR_BASIS] = Transform::toJson(*recordingBasis); // Find the relative transform @@ -1451,7 +1451,7 @@ QJsonObject AvatarData::toJson() const { root[JSON_AVATAR_RELATIVE] = Transform::toJson(avatarTransform); } - auto scale = getTargetScale(); + auto scale = getDomainLimitedScale(); if (scale != 1.0f) { root[JSON_AVATAR_SCALE] = scale; } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index cb3ef0c40e..018833283a 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -243,6 +243,10 @@ public: void setTargetScale(float targetScale); void setTargetScaleVerbose(float targetScale); + float getDomainLimitedScale() const { return glm::clamp(_targetScale, _domainMinimumScale, _domainMaximumScale); } + void setDomainMinimumScale(float domainMinimumScale) { _domainMinimumScale = std::max(domainMinimumScale, MIN_AVATAR_SCALE); } + void setDomainMaximumScale(float domainMaximumScale) { _domainMaximumScale = std::min(domainMaximumScale, MAX_AVATAR_SCALE); } + // Hand State Q_INVOKABLE void setHandState(char s) { _handState = s; } Q_INVOKABLE char getHandState() const { return _handState; } @@ -377,6 +381,8 @@ protected: // Body scale float _targetScale; + float _domainMinimumScale { MIN_AVATAR_SCALE }; + float _domainMaximumScale { MAX_AVATAR_SCALE }; // Hand state (are we grabbing something or not) char _handState; From 64d9fa0f3907a9c03d1968a8903938bb9b7538e1 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 10 Nov 2016 14:19:32 -0800 Subject: [PATCH 18/29] clamp avatar scale to domain limit in avatar mixer --- assignment-client/src/avatars/AvatarMixer.cpp | 28 ++++++++++++++++--- assignment-client/src/avatars/AvatarMixer.h | 3 ++ libraries/avatars/src/AvatarData.h | 6 ++-- 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index d87a5f1cc9..1896e3a43f 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -512,12 +512,19 @@ void AvatarMixer::domainSettingsRequestComplete() { auto nodeList = DependencyManager::get(); nodeList->addNodeTypeToInterestSet(NodeType::Agent); - nodeList->linkedDataCreateCallback = [] (Node* node) { - node->setLinkedData(std::unique_ptr { new AvatarMixerClientData }); - }; - // parse the settings to pull out the values we need parseDomainServerSettings(nodeList->getDomainHandler().getSettingsObject()); + + float domainMinimumScale = _domainMinimumScale; + float domainMaximumScale = _domainMaximumScale; + + nodeList->linkedDataCreateCallback = [domainMinimumScale, domainMaximumScale] (Node* node) { + auto clientData = std::unique_ptr { new AvatarMixerClientData }; + clientData->getAvatar().setDomainMinimumScale(domainMinimumScale); + clientData->getAvatar().setDomainMaximumScale(domainMaximumScale); + + node->setLinkedData(std::move(clientData)); + }; // start the broadcastThread _broadcastThread.start(); @@ -549,4 +556,17 @@ void AvatarMixer::parseDomainServerSettings(const QJsonObject& domainSettings) { _maxKbpsPerNode = nodeBandwidthValue.toDouble(DEFAULT_NODE_SEND_BANDWIDTH) * KILO_PER_MEGA; qDebug() << "The maximum send bandwidth per node is" << _maxKbpsPerNode << "kbps."; + + const QString AVATARS_SETTINGS_KEY = "avatars"; + + static const QString MIN_SCALE_OPTION = "min_avatar_scale"; + float settingMinScale = domainSettings[AVATARS_SETTINGS_KEY].toObject()[MIN_SCALE_OPTION].toDouble(MIN_AVATAR_SCALE); + _domainMinimumScale = glm::clamp(settingMinScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE); + + static const QString MAX_SCALE_OPTION = "max_avatar_scale"; + float settingMaxScale = domainSettings[AVATARS_SETTINGS_KEY].toObject()[MAX_SCALE_OPTION].toDouble(MAX_AVATAR_SCALE); + _domainMaximumScale = glm::clamp(settingMaxScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE); + + qDebug() << "This domain requires a minimum avatar scale of" << _domainMinimumScale + << "and a maximum avatar scale of" << _domainMaximumScale; } diff --git a/assignment-client/src/avatars/AvatarMixer.h b/assignment-client/src/avatars/AvatarMixer.h index 9286cd4691..6e1d722145 100644 --- a/assignment-client/src/avatars/AvatarMixer.h +++ b/assignment-client/src/avatars/AvatarMixer.h @@ -59,6 +59,9 @@ private: float _maxKbpsPerNode = 0.0f; + float _domainMinimumScale { MIN_AVATAR_SCALE }; + float _domainMaximumScale { MAX_AVATAR_SCALE }; + QTimer* _broadcastTimer = nullptr; }; diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 018833283a..97879700ee 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -244,8 +244,10 @@ public: void setTargetScaleVerbose(float targetScale); float getDomainLimitedScale() const { return glm::clamp(_targetScale, _domainMinimumScale, _domainMaximumScale); } - void setDomainMinimumScale(float domainMinimumScale) { _domainMinimumScale = std::max(domainMinimumScale, MIN_AVATAR_SCALE); } - void setDomainMaximumScale(float domainMaximumScale) { _domainMaximumScale = std::min(domainMaximumScale, MAX_AVATAR_SCALE); } + void setDomainMinimumScale(float domainMinimumScale) + { _domainMinimumScale = glm::clamp(domainMinimumScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE); } + void setDomainMaximumScale(float domainMaximumScale) + { _domainMaximumScale = glm::clamp(domainMaximumScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE); } // Hand State Q_INVOKABLE void setHandState(char s) { _handState = s; } From 6f442a74b1cbc7355717ce3cc6a52ede5b8689fc Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 10 Nov 2016 14:43:15 -0800 Subject: [PATCH 19/29] request settings from agents and debug results --- .../resources/describe-settings.json | 1 + interface/src/avatar/MyAvatar.cpp | 3 ++ libraries/networking/src/DomainHandler.cpp | 32 +++++++------------ 3 files changed, 15 insertions(+), 21 deletions(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index b5c612881f..77a33b4d56 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -869,6 +869,7 @@ { "name": "avatars", "label": "Avatars", + "assignment-types": [1, 2], "settings": [ { "name": "min_avatar_scale", diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index e1f0bd3a68..48b6e7c53c 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1893,6 +1893,9 @@ void MyAvatar::restrictScaleFromDomainSettings(const QJsonObject& domainSettings float settingMaxScale = avatarsObject[MAX_SCALE_OPTION].toDouble(MAX_AVATAR_SCALE); setDomainMaximumScale(settingMaxScale); + qCDebug(interfaceapp, "This domain requires a minimum avatar scale of %f and a maximum avatar scale of %f", + _domainMinimumScale, _domainMaximumScale); + // debug to log if this avatar's scale in this domain will be clamped auto clampedScale = glm::clamp(_targetScale, _domainMinimumScale, _domainMaximumScale); diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index eecc1515f5..5d7340b2ce 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -272,27 +272,17 @@ void DomainHandler::setIsConnected(bool isConnected) { } void DomainHandler::requestDomainSettings() { - // TODO: the nodes basically lock if they don't get a response - add a timeout to this so that they at least restart - // if they can't get settings - - NodeType_t owningNodeType = DependencyManager::get()->getOwnerType(); - if (owningNodeType == NodeType::Agent) { - // for now the agent nodes don't need any domain settings - _settingsObject = QJsonObject(); - emit settingsReceived(_settingsObject); - } else { - qCDebug(networking) << "Requesting settings from domain server"; - - Assignment::Type assignmentType = Assignment::typeForNodeType(DependencyManager::get()->getOwnerType()); - - auto packet = NLPacket::create(PacketType::DomainSettingsRequest, sizeof(assignmentType), true, false); - packet->writePrimitive(assignmentType); - - auto nodeList = DependencyManager::get(); - nodeList->sendPacket(std::move(packet), _sockAddr); - - _settingsTimer.start(); - } + qCDebug(networking) << "Requesting settings from domain server"; + + Assignment::Type assignmentType = Assignment::typeForNodeType(DependencyManager::get()->getOwnerType()); + + auto packet = NLPacket::create(PacketType::DomainSettingsRequest, sizeof(assignmentType), true, false); + packet->writePrimitive(assignmentType); + + auto nodeList = DependencyManager::get(); + nodeList->sendPacket(std::move(packet), _sockAddr); + + _settingsTimer.start(); } void DomainHandler::processSettingsPacketList(QSharedPointer packetList) { From 9fdde49d141b4f70ef22d945115c0c5c96f578f4 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 10 Nov 2016 14:54:42 -0800 Subject: [PATCH 20/29] clamp target scale changes on both ends --- interface/src/avatar/MyAvatar.cpp | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 48b6e7c53c..3b2b89310b 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1833,34 +1833,32 @@ bool findAvatarAvatarPenetration(const glm::vec3 positionA, float radiusA, float } void MyAvatar::increaseSize() { - // clamp the target scale to the maximum allowable scale in the domain - + // clamp the target scale to the allowable scale in the domain float updatedTargetScale = _targetScale * (1.0f + SCALING_RATIO); - if (updatedTargetScale > _domainMaximumScale) { - qCDebug(interfaceapp, "Forced scale to %f since %f would be larger than allowed maximum", - _domainMaximumScale, updatedTargetScale); + auto clampedTargetScale = glm::clamp(_targetScale, _domainMinimumScale, _domainMaximumScale); - updatedTargetScale = _domainMaximumScale; + if (clampedTargetScale != updatedTargetScale) { + qCDebug(interfaceapp, "Forcing scale to %f since %f is not allowed by domain", + clampedTargetScale, updatedTargetScale); } - setTargetScale(updatedTargetScale); + setTargetScale(clampedTargetScale); qCDebug(interfaceapp, "Changed scale to %f", (double)_targetScale); } void MyAvatar::decreaseSize() { - // clamp the target scale to the minimum allowable scale in the domain + // clamp the target scale to the allowable scale in the domain float updatedTargetScale = _targetScale * (1.0f - SCALING_RATIO); - if (updatedTargetScale < _domainMinimumScale) { - qCDebug(interfaceapp, "Forced scale to %f since %f would be smaller than allowed minimum", - _domainMinimumScale, updatedTargetScale); + auto clampedTargetScale = glm::clamp(_targetScale, _domainMinimumScale, _domainMaximumScale); - updatedTargetScale = _domainMinimumScale; + if (clampedTargetScale != updatedTargetScale) { + qCDebug(interfaceapp, "Forcing scale to %f since %f is not allowed by domain", + clampedTargetScale, updatedTargetScale); } - - setTargetScale(updatedTargetScale); + setTargetScale(clampedTargetScale); qCDebug(interfaceapp, "Changed scale to %f", (double)_targetScale); } From 53769f4edf030c5a4ddb7aac3cc8ef1c4f551cc3 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 10 Nov 2016 14:58:04 -0800 Subject: [PATCH 21/29] change scale for avatar if domain limited scale changes --- interface/src/avatar/Avatar.cpp | 11 ++++++----- interface/src/avatar/AvatarActionHold.cpp | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 872d2af04e..46ee15de08 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -165,16 +165,17 @@ AABox Avatar::getBounds() const { void Avatar::animateScaleChanges(float deltaTime) { float currentScale = getUniformScale(); - if (currentScale != _targetScale) { - // use exponential decay toward _targetScale + auto desiredScale = getDomainLimitedScale(); + if (currentScale != desiredScale) { + // use exponential decay toward the domain limit clamped scale const float SCALE_ANIMATION_TIMESCALE = 0.5f; float blendFactor = glm::clamp(deltaTime / SCALE_ANIMATION_TIMESCALE, 0.0f, 1.0f); - float animatedScale = (1.0f - blendFactor) * currentScale + blendFactor * _targetScale; + float animatedScale = (1.0f - blendFactor) * currentScale + blendFactor * desiredScale; // snap to the end when we get close enough const float MIN_RELATIVE_SCALE_ERROR = 0.03f; - if (fabsf(_targetScale - currentScale) / _targetScale < MIN_RELATIVE_SCALE_ERROR) { - animatedScale = _targetScale; + if (fabsf(desiredScale - currentScale) / desiredScale < MIN_RELATIVE_SCALE_ERROR) { + animatedScale = desiredScale; } setScale(glm::vec3(animatedScale)); // avatar scale is uniform diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 85ff485d7a..32a3586dba 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -152,7 +152,7 @@ bool AvatarActionHold::getTarget(float deltaTimeStep, glm::quat& rotation, glm:: Transform avatarTransform; auto myAvatar = DependencyManager::get()->getMyAvatar(); avatarTransform = myAvatar->getTransform(); - palmPosition = avatarTransform.transform(pose.getTranslation() / myAvatar->getTargetScale()); + palmPosition = avatarTransform.transform(pose.getTranslation() / myAvatar->getDomainLimitedScale()); palmRotation = avatarTransform.getRotation() * pose.getRotation(); } else { glm::vec3 avatarRigidBodyPosition; From 4d18a3cc3d5a7b52930cfb904676b78688703c83 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Thu, 10 Nov 2016 15:01:06 -0800 Subject: [PATCH 22/29] expose menu enablement to javascript --- .../src/scripting/MenuScriptingInterface.cpp | 17 +++++++++++++++++ .../src/scripting/MenuScriptingInterface.h | 3 +++ libraries/ui/src/ui/Menu.cpp | 19 +++++++++++++++++++ libraries/ui/src/ui/Menu.h | 3 +++ 4 files changed, 42 insertions(+) diff --git a/interface/src/scripting/MenuScriptingInterface.cpp b/interface/src/scripting/MenuScriptingInterface.cpp index 2fa7470561..df75d331d6 100644 --- a/interface/src/scripting/MenuScriptingInterface.cpp +++ b/interface/src/scripting/MenuScriptingInterface.cpp @@ -126,6 +126,23 @@ void MenuScriptingInterface::setIsOptionChecked(const QString& menuOption, bool Q_ARG(bool, isChecked)); } +bool MenuScriptingInterface::isMenuEnabled(const QString& menuOption) { + if (QThread::currentThread() == qApp->thread()) { + return Menu::getInstance()->isOptionChecked(menuOption); + } + bool result; + QMetaObject::invokeMethod(Menu::getInstance(), "isMenuEnabled", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(bool, result), + Q_ARG(const QString&, menuOption)); + return result; +} + +void MenuScriptingInterface::setMenuEnabled(const QString& menuOption, bool isChecked) { + QMetaObject::invokeMethod(Menu::getInstance(), "setMenuEnabled", + Q_ARG(const QString&, menuOption), + Q_ARG(bool, isChecked)); +} + void MenuScriptingInterface::triggerOption(const QString& menuOption) { QMetaObject::invokeMethod(Menu::getInstance(), "triggerOption", Q_ARG(const QString&, menuOption)); } diff --git a/interface/src/scripting/MenuScriptingInterface.h b/interface/src/scripting/MenuScriptingInterface.h index 5b8a437529..855b1af13b 100644 --- a/interface/src/scripting/MenuScriptingInterface.h +++ b/interface/src/scripting/MenuScriptingInterface.h @@ -50,6 +50,9 @@ public slots: void setIsOptionChecked(const QString& menuOption, bool isChecked); void triggerOption(const QString& menuOption); + + bool isMenuEnabled(const QString& menuName); + void setMenuEnabled(const QString& menuName, bool isEnabled); signals: void menuItemEvent(const QString& menuItem); diff --git a/libraries/ui/src/ui/Menu.cpp b/libraries/ui/src/ui/Menu.cpp index aee8b40832..ba24adfc3f 100644 --- a/libraries/ui/src/ui/Menu.cpp +++ b/libraries/ui/src/ui/Menu.cpp @@ -428,6 +428,25 @@ bool Menu::menuExists(const QString& menuName) { return false; } +bool Menu::isMenuEnabled(const QString& menuName) { + QAction* action = getMenuAction(menuName); + + // only proceed if the menu actually exists + if (action) { + return action->isEnabled(); + } + return false; +} + +void Menu::setMenuEnabled(const QString& menuName, bool isEnabled) { + QAction* action = getMenuAction(menuName); + + // only proceed if the menu actually exists + if (action) { + action->setEnabled(isEnabled); + } +} + void Menu::addSeparator(const QString& menuName, const QString& separatorName, const QString& grouping) { MenuWrapper* menuObj = getMenu(menuName); if (menuObj) { diff --git a/libraries/ui/src/ui/Menu.h b/libraries/ui/src/ui/Menu.h index ee60a031c3..2711fc5921 100644 --- a/libraries/ui/src/ui/Menu.h +++ b/libraries/ui/src/ui/Menu.h @@ -106,6 +106,9 @@ public slots: bool isOptionChecked(const QString& menuOption) const; void setIsOptionChecked(const QString& menuOption, bool isChecked); + bool isMenuEnabled(const QString& menuName); + void setMenuEnabled(const QString& menuName, bool isEnabled); + bool getGroupingIsVisible(const QString& grouping); void setGroupingIsVisible(const QString& grouping, bool isVisible); /// NOTE: the "" grouping is always visible From d5722c1654dd55f1ab8235ec2f5a7c09afb5887b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 10 Nov 2016 15:03:13 -0800 Subject: [PATCH 23/29] fix clamping of scale and DRY it up --- interface/src/avatar/MyAvatar.cpp | 44 +++++++++++-------------------- interface/src/avatar/MyAvatar.h | 1 + 2 files changed, 17 insertions(+), 28 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 3b2b89310b..0fd7192f2f 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1832,49 +1832,37 @@ bool findAvatarAvatarPenetration(const glm::vec3 positionA, float radiusA, float return false; } +void MyAvatar::clampScaleChangeToDomainLimits(float desiredScale) { + auto clampedTargetScale = glm::clamp(desiredScale, _domainMinimumScale, _domainMaximumScale); + + if (clampedTargetScale != desiredScale) { + qCDebug(interfaceapp, "Forcing scale to %f since %f is not allowed by domain", + clampedTargetScale, desiredScale); + } + + setTargetScale(clampedTargetScale); + qCDebug(interfaceapp, "Changed scale to %f", _targetScale); +} + void MyAvatar::increaseSize() { // clamp the target scale to the allowable scale in the domain float updatedTargetScale = _targetScale * (1.0f + SCALING_RATIO); - auto clampedTargetScale = glm::clamp(_targetScale, _domainMinimumScale, _domainMaximumScale); - - if (clampedTargetScale != updatedTargetScale) { - qCDebug(interfaceapp, "Forcing scale to %f since %f is not allowed by domain", - clampedTargetScale, updatedTargetScale); - } - - setTargetScale(clampedTargetScale); - qCDebug(interfaceapp, "Changed scale to %f", (double)_targetScale); + clampScaleChangeToDomainLimits(updatedTargetScale); } void MyAvatar::decreaseSize() { // clamp the target scale to the allowable scale in the domain float updatedTargetScale = _targetScale * (1.0f - SCALING_RATIO); - auto clampedTargetScale = glm::clamp(_targetScale, _domainMinimumScale, _domainMaximumScale); - - if (clampedTargetScale != updatedTargetScale) { - qCDebug(interfaceapp, "Forcing scale to %f since %f is not allowed by domain", - clampedTargetScale, updatedTargetScale); - } - - setTargetScale(clampedTargetScale); - qCDebug(interfaceapp, "Changed scale to %f", (double)_targetScale); + clampScaleChangeToDomainLimits(updatedTargetScale); } void MyAvatar::resetSize() { - // if the default + // attempt to reset avatar size to the default const float DEFAULT_AVATAR_SCALE = 1.0f; - float allowedDefaultScale = glm::clamp(DEFAULT_AVATAR_SCALE, _domainMinimumScale, _domainMaximumScale); - - if (allowedDefaultScale != DEFAULT_AVATAR_SCALE) { - qCDebug(interfaceapp, "Forcing scale to %f since %f is not an allowed avatar scale by the domain", - allowedDefaultScale, DEFAULT_AVATAR_SCALE); - } - - setTargetScale(allowedDefaultScale); - qCDebug(interfaceapp, "Reset scale to %f", (double)_targetScale); + clampScaleChangeToDomainLimits(DEFAULT_AVATAR_SCALE); } void MyAvatar::restrictScaleFromDomainSettings(const QJsonObject& domainSettingsObject) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index d9c88621d2..0b8db6a24d 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -372,6 +372,7 @@ private: virtual void updatePalms() override {} void lateUpdatePalms(); + void clampScaleChangeToDomainLimits(float desiredScale); float _driveKeys[MAX_DRIVE_KEYS]; bool _wasPushing; From 8299e23cc3e084f2be5823491963bf5606c4028c Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Thu, 10 Nov 2016 15:15:11 -0800 Subject: [PATCH 24/29] diable some viewpoint menu options when in hmd. --- scripts/system/hmd.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/system/hmd.js b/scripts/system/hmd.js index 84ff6b3c89..e1c846806f 100644 --- a/scripts/system/hmd.js +++ b/scripts/system/hmd.js @@ -24,10 +24,16 @@ var desktopMenuItemName = "Desktop"; var toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); var button; +// Independent and Entity mode make people sick. Third Person and Mirror have traps that we need to work through. +// Disable them in hmd. +var desktopOnlyViews = ['Third Person', 'Mirror', 'Independent Mode', 'Entity Mode']; function onHmdChanged(isHmd) { button.writeProperty('buttonState', isHmd ? 0 : 1); button.writeProperty('defaultState', isHmd ? 0 : 1); button.writeProperty('hoverState', isHmd ? 2 : 3); + desktopOnlyViews.forEach(function (view) { + Menu.setMenuEnabled("View>" + view, !isHmd); + }); } function onClicked(){ var isDesktop = Menu.isOptionChecked(desktopMenuItemName); From 272d0c845e48d58db7e1089fa04c8d22dacb59e5 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 10 Nov 2016 15:25:15 -0800 Subject: [PATCH 25/29] add clarifying comments about target scale changes --- interface/src/avatar/MyAvatar.cpp | 36 ++++++++++++++++++++++++++++--- interface/src/avatar/MyAvatar.h | 1 + 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 0fd7192f2f..cdde8a00a3 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1832,6 +1832,28 @@ bool findAvatarAvatarPenetration(const glm::vec3 positionA, float radiusA, float return false; } +// There can be a separation between the _targetScale and the actual scale of the rendered avatar in a domain. +// When the avatar enters a domain where their target scale is not allowed according to the min/max +// we do not change their saved target scale. Instead, we use getDomainLimitedScale() to render the avatar +// at a domain appropriate size. When the avatar leaves the limiting domain, we'll return them to their previous target scale. +// While connected to a domain that limits avatar scale if the user manually changes their avatar scale, we change +// target scale to match the new scale they have chosen. When they leave the domain they will not return to the scale they were +// before they entered the limiting domain. + +void MyAvatar::clampTargetScaleToDomainLimits() { + // when we're about to change the target scale because the user has asked to increase or decrease their scale, + // we first make sure that we're starting from a target scale that is allowed by the current domain + + auto clampedTargetScale = glm::clamp(_targetScale, _domainMinimumScale, _domainMaximumScale); + + if (clampedTargetScale != _targetScale) { + qCDebug(interfaceapp, "Clamped scale to %f since original target scale %f was not allowed by domain", + clampedTargetScale, _targetScale); + + setTargetScale(clampedTargetScale); + } +} + void MyAvatar::clampScaleChangeToDomainLimits(float desiredScale) { auto clampedTargetScale = glm::clamp(desiredScale, _domainMinimumScale, _domainMaximumScale); @@ -1845,21 +1867,29 @@ void MyAvatar::clampScaleChangeToDomainLimits(float desiredScale) { } void MyAvatar::increaseSize() { - // clamp the target scale to the allowable scale in the domain + // make sure we're starting from an allowable scale + clampTargetScaleToDomainLimits(); + + // calculate what our new scale should be float updatedTargetScale = _targetScale * (1.0f + SCALING_RATIO); + // attempt to change to desired scale (clamped to the domain limits) clampScaleChangeToDomainLimits(updatedTargetScale); } void MyAvatar::decreaseSize() { - // clamp the target scale to the allowable scale in the domain + // make sure we're starting from an allowable scale + clampTargetScaleToDomainLimits(); + + // calculate what our new scale should be float updatedTargetScale = _targetScale * (1.0f - SCALING_RATIO); + // attempt to change to desired scale (clamped to the domain limits) clampScaleChangeToDomainLimits(updatedTargetScale); } void MyAvatar::resetSize() { - // attempt to reset avatar size to the default + // attempt to reset avatar size to the default (clamped to domain limits) const float DEFAULT_AVATAR_SCALE = 1.0f; clampScaleChangeToDomainLimits(DEFAULT_AVATAR_SCALE); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 0b8db6a24d..60049bea67 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -372,6 +372,7 @@ private: virtual void updatePalms() override {} void lateUpdatePalms(); + void clampTargetScaleToDomainLimits(); void clampScaleChangeToDomainLimits(float desiredScale); float _driveKeys[MAX_DRIVE_KEYS]; From f14037979a697d6c72b7e24c98a2dbb42833f81c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 10 Nov 2016 15:31:23 -0800 Subject: [PATCH 26/29] fix a typo in help text for minimum scale --- domain-server/resources/describe-settings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 77a33b4d56..8b3b21e066 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -875,7 +875,7 @@ "name": "min_avatar_scale", "type": "double", "label": "Minimum Avatar Scale (meters)", - "help": "Limits the scale of avatars in your domain. Must be at least than 0.005.", + "help": "Limits the scale of avatars in your domain. Must be at least 0.005.", "placeholder": 0.25, "default": 0.25 }, From 88c63332de1cda9058fec5f789d33a9a733cf619 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 10 Nov 2016 16:21:48 -0800 Subject: [PATCH 27/29] protect flipped min/max scale in avatar mixer settings --- assignment-client/src/avatars/AvatarMixer.cpp | 3 +++ interface/src/avatar/MyAvatar.cpp | 3 +++ 2 files changed, 6 insertions(+) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 1896e3a43f..9700c544c1 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -567,6 +567,9 @@ void AvatarMixer::parseDomainServerSettings(const QJsonObject& domainSettings) { float settingMaxScale = domainSettings[AVATARS_SETTINGS_KEY].toObject()[MAX_SCALE_OPTION].toDouble(MAX_AVATAR_SCALE); _domainMaximumScale = glm::clamp(settingMaxScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE); + // make sure that the domain owner didn't flip min and max + _domainMinimumScale = std::min(_domainMinimumScale, _domainMaximumScale); + qDebug() << "This domain requires a minimum avatar scale of" << _domainMinimumScale << "and a maximum avatar scale of" << _domainMaximumScale; } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index cdde8a00a3..7ce0f92d72 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1909,6 +1909,9 @@ void MyAvatar::restrictScaleFromDomainSettings(const QJsonObject& domainSettings float settingMaxScale = avatarsObject[MAX_SCALE_OPTION].toDouble(MAX_AVATAR_SCALE); setDomainMaximumScale(settingMaxScale); + // make sure that the domain owner didn't flip min and max + _domainMinimumScale = std::min(_domainMinimumScale, _domainMaximumScale); + qCDebug(interfaceapp, "This domain requires a minimum avatar scale of %f and a maximum avatar scale of %f", _domainMinimumScale, _domainMaximumScale); From 020942c82602c98ea36bc475ffeddf492703e062 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 10 Nov 2016 16:50:36 -0800 Subject: [PATCH 28/29] better handling for a flipped min/max scale --- assignment-client/src/avatars/AvatarMixer.cpp | 4 +++- interface/src/avatar/MyAvatar.cpp | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 9700c544c1..041449dc57 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -568,7 +568,9 @@ void AvatarMixer::parseDomainServerSettings(const QJsonObject& domainSettings) { _domainMaximumScale = glm::clamp(settingMaxScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE); // make sure that the domain owner didn't flip min and max - _domainMinimumScale = std::min(_domainMinimumScale, _domainMaximumScale); + if (_domainMinimumScale > _domainMaximumScale) { + std::swap(_domainMinimumScale, _domainMaximumScale); + } qDebug() << "This domain requires a minimum avatar scale of" << _domainMinimumScale << "and a maximum avatar scale of" << _domainMaximumScale; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 7ce0f92d72..155b059f68 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1910,7 +1910,9 @@ void MyAvatar::restrictScaleFromDomainSettings(const QJsonObject& domainSettings setDomainMaximumScale(settingMaxScale); // make sure that the domain owner didn't flip min and max - _domainMinimumScale = std::min(_domainMinimumScale, _domainMaximumScale); + if (_domainMinimumScale > _domainMaximumScale) { + std::swap(_domainMinimumScale, _domainMaximumScale); + } qCDebug(interfaceapp, "This domain requires a minimum avatar scale of %f and a maximum avatar scale of %f", _domainMinimumScale, _domainMaximumScale); From 7f46714645d3a259b369d8f89488a17dcd948050 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 10 Nov 2016 16:53:56 -0800 Subject: [PATCH 29/29] drop the incorrect scale from domain settings --- domain-server/resources/describe-settings.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 8b3b21e066..675c39563b 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -874,7 +874,7 @@ { "name": "min_avatar_scale", "type": "double", - "label": "Minimum Avatar Scale (meters)", + "label": "Minimum Avatar Scale", "help": "Limits the scale of avatars in your domain. Must be at least 0.005.", "placeholder": 0.25, "default": 0.25 @@ -882,7 +882,7 @@ { "name": "max_avatar_scale", "type": "double", - "label": "Maximum Avatar Scale (meters)", + "label": "Maximum Avatar Scale", "help": "Limits the scale of avatars in your domain. Cannot be greater than 1000.", "placeholder": 3.0, "default": 3.0