From 2952be5fc89f86d67a67e3d58bd54980e4e66e63 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 7 Feb 2017 12:02:41 -0800 Subject: [PATCH 1/3] Added MouseX and MouseY axes to Keyboard device. --- .../input-plugins/src/input-plugins/KeyboardMouseDevice.cpp | 6 ++++++ .../input-plugins/src/input-plugins/KeyboardMouseDevice.h | 2 ++ 2 files changed, 8 insertions(+) diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp index 3308ba36ab..ddb2f482a1 100755 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp @@ -25,6 +25,9 @@ void KeyboardMouseDevice::pluginUpdate(float deltaTime, const controller::InputC auto userInputMapper = DependencyManager::get(); userInputMapper->withLock([&, this]() { _inputDevice->update(deltaTime, inputCalibrationData); + + _inputDevice->_axisStateMap[MOUSE_AXIS_X] = _lastCursor.x(); + _inputDevice->_axisStateMap[MOUSE_AXIS_Y] = _lastCursor.y(); }); // For touch event, we need to check that the last event is not too long ago @@ -249,6 +252,9 @@ controller::Input::NamedVector KeyboardMouseDevice::InputDevice::getAvailableInp availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_Y_POS), "MouseMoveUp")); availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_Y_NEG), "MouseMoveDown")); + availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_X), "MouseX")); + availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_Y), "MouseY")); + availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_WHEEL_Y_POS), "MouseWheelRight")); availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_WHEEL_Y_NEG), "MouseWheelLeft")); availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_WHEEL_X_POS), "MouseWheelUp")); diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h index f38b43c107..3570ec7193 100644 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h @@ -47,6 +47,8 @@ public: MOUSE_AXIS_X_NEG, MOUSE_AXIS_Y_POS, MOUSE_AXIS_Y_NEG, + MOUSE_AXIS_X, + MOUSE_AXIS_Y, MOUSE_AXIS_WHEEL_Y_POS, MOUSE_AXIS_WHEEL_Y_NEG, MOUSE_AXIS_WHEEL_X_POS, From 7d630f6c9a083b0c25caad5c51b83c486e406194 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 7 Feb 2017 12:03:28 -0800 Subject: [PATCH 2/3] Added Mat4.createFromColumns --- libraries/script-engine/src/Mat4.cpp | 4 ++++ libraries/script-engine/src/Mat4.h | 2 ++ 2 files changed, 6 insertions(+) diff --git a/libraries/script-engine/src/Mat4.cpp b/libraries/script-engine/src/Mat4.cpp index bb65cb1e26..52b9690321 100644 --- a/libraries/script-engine/src/Mat4.cpp +++ b/libraries/script-engine/src/Mat4.cpp @@ -26,6 +26,10 @@ glm::mat4 Mat4::createFromScaleRotAndTrans(const glm::vec3& scale, const glm::qu return createMatFromScaleQuatAndPos(scale, rot, trans); } +glm::mat4 Mat4::createFromColumns(const glm::vec4& col0, const glm::vec4& col1, const glm::vec4& col2, const glm::vec4& col3) const { + return glm::mat4(col0, col1, col2, col3); +} + glm::vec3 Mat4::extractTranslation(const glm::mat4& m) const { return ::extractTranslation(m); } diff --git a/libraries/script-engine/src/Mat4.h b/libraries/script-engine/src/Mat4.h index 047bf56079..8b2a8aa8c1 100644 --- a/libraries/script-engine/src/Mat4.h +++ b/libraries/script-engine/src/Mat4.h @@ -23,8 +23,10 @@ class Mat4 : public QObject { public slots: glm::mat4 multiply(const glm::mat4& m1, const glm::mat4& m2) const; + glm::mat4 createFromRotAndTrans(const glm::quat& rot, const glm::vec3& trans) const; glm::mat4 createFromScaleRotAndTrans(const glm::vec3& scale, const glm::quat& rot, const glm::vec3& trans) const; + glm::mat4 createFromColumns(const glm::vec4& col0, const glm::vec4& col1, const glm::vec4& col2, const glm::vec4& col3) const; glm::vec3 extractTranslation(const glm::mat4& m) const; glm::quat extractRotation(const glm::mat4& m) const; From cb58c567b837f1dff257afd4e921c1af940aff0e Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 7 Feb 2017 18:18:34 -0800 Subject: [PATCH 3/3] In desktop mode create tablet near mouse click location. But prevent the tablet from being displayed off screen. --- scripts/system/libraries/WebTablet.js | 75 +++++++++++++++++++++++---- 1 file changed, 65 insertions(+), 10 deletions(-) diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index 0cd7d26854..74bbd788be 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -8,7 +8,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // /* global getControllerWorldLocation, setEntityCustomData, Tablet, WebTablet:true, HMD, Settings, Script, - Vec3, Quat, MyAvatar, Entities, Overlays, Camera, Messages, Xform */ + Vec3, Quat, MyAvatar, Entities, Overlays, Camera, Messages, Xform, clamp */ Script.include(Script.resolvePath("../libraries/utils.js")); Script.include(Script.resolvePath("../libraries/controllers.js")); @@ -118,7 +118,7 @@ WebTablet = function (url, width, dpi, hand, clientOnly) { }; // compute position, rotation & parentJointIndex of the tablet - this.calculateTabletAttachmentProperties(hand, tabletProperties); + this.calculateTabletAttachmentProperties(hand, true, tabletProperties); this.cleanUpOldTablets(); this.tabletEntityID = Entities.addEntity(tabletProperties, clientOnly); @@ -252,31 +252,78 @@ WebTablet.prototype.destroy = function () { WebTablet.prototype.geometryChanged = function (geometry) { if (!HMD.active) { var tabletProperties = {}; + // compute position, rotation & parentJointIndex of the tablet - this.calculateTabletAttachmentProperties(NO_HANDS, tabletProperties); + this.calculateTabletAttachmentProperties(NO_HANDS, false, tabletProperties); Entities.editEntity(this.tabletEntityID, tabletProperties); } }; +function gluPerspective(fovy, aspect, zNear, zFar) { + var cotan = 1 / Math.tan(fovy / 2); + var alpha = -(zFar + zNear) / (zFar - zNear); + var beta = -(2 * zFar * zNear) / (zFar - zNear); + var col0 = {x: cotan / aspect, y: 0, z: 0, w: 0}; + var col1 = {x: 0, y: cotan, z: 0, w: 0}; + var col2 = {x: 0, y: 0, z: alpha, w: -1}; + var col3 = {x: 0, y: 0, z: beta, w: 0}; + return Mat4.createFromColumns(col0, col1, col2, col3); +} + // calclulate the appropriate position of the tablet in world space, such that it fits in the center of the screen. // with a bit of padding on the top and bottom. -WebTablet.prototype.calculateWorldAttitudeRelativeToCamera = function () { +// windowPos is used to position the center of the tablet at the given position. +WebTablet.prototype.calculateWorldAttitudeRelativeToCamera = function (windowPos) { + var DEFAULT_DESKTOP_TABLET_SCALE = 75; var DESKTOP_TABLET_SCALE = Settings.getValue("desktopTabletScale") || DEFAULT_DESKTOP_TABLET_SCALE; + + // clamp window pos so 2d tablet is not off-screen. + var TABLET_TEXEL_PADDING = {x: 60, y: 90}; + var X_CLAMP = (DESKTOP_TABLET_SCALE / 100) * ((TABLET_TEXTURE_RESOLUTION.x / 2) + TABLET_TEXEL_PADDING.x); + var Y_CLAMP = (DESKTOP_TABLET_SCALE / 100) * ((TABLET_TEXTURE_RESOLUTION.y / 2) + TABLET_TEXEL_PADDING.y); + windowPos.x = clamp(windowPos.x, X_CLAMP, Window.innerWidth - X_CLAMP); + windowPos.y = clamp(windowPos.y, Y_CLAMP, Window.innerHeight - Y_CLAMP); + var fov = (Settings.getValue('fieldOfView') || DEFAULT_VERTICAL_FIELD_OF_VIEW) * (Math.PI / 180); var MAX_PADDING_FACTOR = 2.2; var PADDING_FACTOR = Math.min(Window.innerHeight / TABLET_TEXTURE_RESOLUTION.y, MAX_PADDING_FACTOR); var TABLET_HEIGHT = (TABLET_TEXTURE_RESOLUTION.y / this.dpi) * INCHES_TO_METERS; var WEB_ENTITY_Z_OFFSET = (this.depth / 2); + + // calcualte distance from camera var dist = (PADDING_FACTOR * TABLET_HEIGHT) / (2 * Math.tan(fov / 2) * (DESKTOP_TABLET_SCALE / 100)) - WEB_ENTITY_Z_OFFSET; + + var Z_NEAR = 0.01; + var Z_FAR = 100.0; + + // calculate mouse position in clip space + var alpha = -(Z_FAR + Z_NEAR) / (Z_FAR - Z_NEAR); + var beta = -(2 * Z_FAR * Z_NEAR) / (Z_FAR - Z_NEAR); + var clipZ = (beta / dist) - alpha; + var clipMousePosition = {x: (2 * windowPos.x / Window.innerWidth) - 1, + y: (2 * ((Window.innerHeight - windowPos.y) / Window.innerHeight)) - 1, + z: clipZ}; + + // calculate projection matrix + var aspect = Window.innerWidth / Window.innerHeight; + var projMatrix = gluPerspective(fov, aspect, Z_NEAR, Z_FAR); + + // transform mouse clip position into view coordinates. + var viewMousePosition = Mat4.transformPoint(Mat4.inverse(projMatrix), clipMousePosition); + + // transform view mouse position into world coordinates. + var viewToWorldMatrix = Mat4.createFromRotAndTrans(Camera.orientation, Camera.position); + var worldMousePosition = Mat4.transformPoint(viewToWorldMatrix, viewMousePosition); + return { - position: Vec3.sum(Camera.position, Vec3.multiply(dist, Quat.getFront(Camera.orientation))), + position: worldMousePosition, rotation: Quat.multiply(Camera.orientation, ROT_Y_180) }; }; // compute position, rotation & parentJointIndex of the tablet -WebTablet.prototype.calculateTabletAttachmentProperties = function (hand, tabletProperties) { +WebTablet.prototype.calculateTabletAttachmentProperties = function (hand, useMouse, tabletProperties) { if (HMD.active) { // in HMD mode, the tablet should be relative to the sensor to world matrix. tabletProperties.parentJointIndex = SENSOR_TO_ROOM_MATRIX; @@ -289,8 +336,16 @@ WebTablet.prototype.calculateTabletAttachmentProperties = function (hand, tablet // in desktop mode, the tablet should be relative to the camera tabletProperties.parentJointIndex = CAMERA_MATRIX; - // compute the appropriate postion of the tablet such that it fits in the center of the screen nicely. - var attitude = this.calculateWorldAttitudeRelativeToCamera(); + var windowPos; + if (useMouse) { + // compute the appropriate postion of the tablet such that it fits in the center of the screen nicely. + windowPos = {x: Controller.getValue(Controller.Hardware.Keyboard.MouseX), + y: Controller.getValue(Controller.Hardware.Keyboard.MouseY)}; + } else { + windowPos = {x: Window.innerWidth / 2, + y: Window.innerHeight / 2}; + } + var attitude = this.calculateWorldAttitudeRelativeToCamera(windowPos); tabletProperties.position = attitude.position; tabletProperties.rotation = attitude.rotation; } @@ -310,7 +365,7 @@ WebTablet.prototype.onHmdChanged = function () { var tabletProperties = {}; // compute position, rotation & parentJointIndex of the tablet - this.calculateTabletAttachmentProperties(NO_HANDS, tabletProperties); + this.calculateTabletAttachmentProperties(NO_HANDS, false, tabletProperties); Entities.editEntity(this.tabletEntityID, tabletProperties); // Full scene FXAA should be disabled on the overlay when the tablet in desktop mode. @@ -398,7 +453,7 @@ WebTablet.prototype.cameraModeChanged = function (newMode) { var self = this; var tabletProperties = {}; // compute position, rotation & parentJointIndex of the tablet - self.calculateTabletAttachmentProperties(NO_HANDS, tabletProperties); + self.calculateTabletAttachmentProperties(NO_HANDS, false, tabletProperties); Entities.editEntity(self.tabletEntityID, tabletProperties); } };