mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-08 23:02:32 +02:00
Merge pull request #8240 from jherico/hand_controller
Menu & hand controller polish
This commit is contained in:
commit
7bb31fe075
15 changed files with 182 additions and 26 deletions
|
@ -3,9 +3,17 @@
|
|||
"channels": [
|
||||
{ "from": "Hydra.LY", "filters": "invert", "to": "Standard.LY" },
|
||||
{ "from": "Hydra.LX", "to": "Standard.LX" },
|
||||
{ "from": "Hydra.LT", "to": "Standard.LTClick",
|
||||
"peek": true,
|
||||
"filters": [ { "type": "hysteresis", "min": 0.85, "max": 0.9 } ]
|
||||
},
|
||||
{ "from": "Hydra.LT", "to": "Standard.LT" },
|
||||
{ "from": "Hydra.RY", "filters": "invert", "to": "Standard.RY" },
|
||||
{ "from": "Hydra.RX", "to": "Standard.RX" },
|
||||
{ "from": "Hydra.RT", "to": "Standard.RTClick",
|
||||
"peek": true,
|
||||
"filters": [ { "type": "hysteresis", "min": 0.85, "max": 0.9 } ]
|
||||
},
|
||||
{ "from": "Hydra.RT", "to": "Standard.RT" },
|
||||
|
||||
{ "from": "Hydra.LB", "to": "Standard.LB" },
|
||||
|
|
|
@ -8,6 +8,10 @@
|
|||
|
||||
{ "from": "OculusTouch.LY", "filters": "invert", "to": "Standard.LY" },
|
||||
{ "from": "OculusTouch.LX", "to": "Standard.LX" },
|
||||
{ "from": "OculusTouch.LT", "to": "Standard.LTClick",
|
||||
"peek": true,
|
||||
"filters": [ { "type": "hysteresis", "min": 0.85, "max": 0.9 } ]
|
||||
},
|
||||
{ "from": "OculusTouch.LT", "to": "Standard.LT" },
|
||||
{ "from": "OculusTouch.LS", "to": "Standard.LS" },
|
||||
{ "from": "OculusTouch.LeftGrip", "to": "Standard.LeftGrip" },
|
||||
|
@ -15,6 +19,10 @@
|
|||
|
||||
{ "from": "OculusTouch.RY", "filters": "invert", "to": "Standard.RY" },
|
||||
{ "from": "OculusTouch.RX", "to": "Standard.RX" },
|
||||
{ "from": "OculusTouch.RT", "to": "Standard.RTClick",
|
||||
"peek": true,
|
||||
"filters": [ { "type": "hysteresis", "min": 0.85, "max": 0.9 } ]
|
||||
},
|
||||
{ "from": "OculusTouch.RT", "to": "Standard.RT" },
|
||||
{ "from": "OculusTouch.RS", "to": "Standard.RS" },
|
||||
{ "from": "OculusTouch.RightGrip", "to": "Standard.RightGrip" },
|
||||
|
|
|
@ -10,14 +10,7 @@
|
|||
{ "from": "Standard.RB", "to": "Actions.UiNavGroup" },
|
||||
{ "from": [ "Standard.A", "Standard.X" ], "to": "Actions.UiNavSelect" },
|
||||
{ "from": [ "Standard.B", "Standard.Y" ], "to": "Actions.UiNavBack" },
|
||||
{
|
||||
"from": [ "Standard.RT", "Standard.LT" ],
|
||||
"to": "Actions.UiNavSelect",
|
||||
"filters": [
|
||||
{ "type": "deadZone", "min": 0.5 },
|
||||
"constrainToInteger"
|
||||
]
|
||||
},
|
||||
{ "from": [ "Standard.RTClick", "Standard.LTClick" ], "to": "Actions.UiNavSelect" },
|
||||
{
|
||||
"from": "Standard.LX", "to": "Actions.UiNavLateral",
|
||||
"filters": [
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
{ "type": "deadZone", "min": 0.05 }
|
||||
]
|
||||
},
|
||||
{ "from": "Vive.LTClick", "to": "Standard.LTClick" },
|
||||
|
||||
{ "from": "Vive.LeftGrip", "to": "Standard.LeftGrip" },
|
||||
{ "from": "Vive.LS", "to": "Standard.LS" },
|
||||
{ "from": "Vive.LSTouch", "to": "Standard.LSTouch" },
|
||||
|
@ -21,6 +23,8 @@
|
|||
{ "type": "deadZone", "min": 0.05 }
|
||||
]
|
||||
},
|
||||
{ "from": "Vive.RTClick", "to": "Standard.RTClick" },
|
||||
|
||||
{ "from": "Vive.RightGrip", "to": "Standard.RightGrip" },
|
||||
{ "from": "Vive.RS", "to": "Standard.RS" },
|
||||
{ "from": "Vive.RSTouch", "to": "Standard.RSTouch" },
|
||||
|
|
|
@ -45,6 +45,7 @@ FocusScope {
|
|||
onVisibleChanged: recalcSize();
|
||||
onCountChanged: recalcSize();
|
||||
focus: true
|
||||
highlightMoveDuration: 0
|
||||
|
||||
highlight: Rectangle {
|
||||
anchors {
|
||||
|
|
|
@ -4705,6 +4705,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
|
|||
qScriptRegisterMetaType(scriptEngine, RayToOverlayIntersectionResultToScriptValue,
|
||||
RayToOverlayIntersectionResultFromScriptValue);
|
||||
|
||||
scriptEngine->registerGlobalObject("OffscreenFlags", DependencyManager::get<OffscreenUi>()->getFlags());
|
||||
scriptEngine->registerGlobalObject("Desktop", DependencyManager::get<DesktopScriptingInterface>().data());
|
||||
scriptEngine->registerGlobalObject("Toolbars", DependencyManager::get<ToolbarScriptingInterface>().data());
|
||||
|
||||
|
|
|
@ -65,6 +65,8 @@ Input::NamedVector StandardController::getAvailableInputs() const {
|
|||
// Triggers
|
||||
makePair(LT, "LT"),
|
||||
makePair(RT, "RT"),
|
||||
makePair(LT_CLICK, "LTClick"),
|
||||
makePair(RT_CLICK, "RTClick"),
|
||||
|
||||
// Finger abstractions
|
||||
makePair(LEFT_PRIMARY_THUMB, "LeftPrimaryThumb"),
|
||||
|
|
|
@ -46,6 +46,7 @@ namespace controller {
|
|||
LS_CENTER,
|
||||
LS_X,
|
||||
LS_Y,
|
||||
LT_CLICK,
|
||||
|
||||
RIGHT_PRIMARY_THUMB,
|
||||
RIGHT_SECONDARY_THUMB,
|
||||
|
@ -56,6 +57,7 @@ namespace controller {
|
|||
RS_CENTER,
|
||||
RS_X,
|
||||
RS_Y,
|
||||
RT_CLICK,
|
||||
|
||||
LEFT_PRIMARY_INDEX,
|
||||
LEFT_SECONDARY_INDEX,
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "filters/InvertFilter.h"
|
||||
#include "filters/PulseFilter.h"
|
||||
#include "filters/ScaleFilter.h"
|
||||
#include "conditionals/AndConditional.h"
|
||||
|
||||
using namespace controller;
|
||||
|
||||
|
@ -58,12 +59,22 @@ QObject* RouteBuilderProxy::peek(bool enable) {
|
|||
}
|
||||
|
||||
QObject* RouteBuilderProxy::when(const QScriptValue& expression) {
|
||||
_route->conditional = _parent.conditionalFor(expression);
|
||||
auto newConditional = _parent.conditionalFor(expression);
|
||||
if (_route->conditional) {
|
||||
_route->conditional = ConditionalPointer(new AndConditional(_route->conditional, newConditional));
|
||||
} else {
|
||||
_route->conditional = newConditional;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
QObject* RouteBuilderProxy::whenQml(const QJSValue& expression) {
|
||||
_route->conditional = _parent.conditionalFor(expression);
|
||||
auto newConditional = _parent.conditionalFor(expression);
|
||||
if (_route->conditional) {
|
||||
_route->conditional = ConditionalPointer(new AndConditional(_route->conditional, newConditional));
|
||||
} else {
|
||||
_route->conditional = newConditional;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,9 @@ class OffscreenFlags : public QObject {
|
|||
Q_OBJECT
|
||||
Q_PROPERTY(bool navigationFocused READ isNavigationFocused WRITE setNavigationFocused NOTIFY navigationFocusedChanged)
|
||||
|
||||
// Allow scripts that are doing their own navigation support to disable navigation focus (i.e. handControllerPointer.js)
|
||||
Q_PROPERTY(bool navigationFocusDisabled READ isNavigationFocusDisabled WRITE setNavigationFocusDisabled NOTIFY navigationFocusDisabledChanged)
|
||||
|
||||
public:
|
||||
|
||||
OffscreenFlags(QObject* parent = nullptr) : QObject(parent) {}
|
||||
|
@ -40,11 +43,21 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
bool isNavigationFocusDisabled() const { return _navigationFocusDisabled; }
|
||||
void setNavigationFocusDisabled(bool disabled) {
|
||||
if (_navigationFocusDisabled != disabled) {
|
||||
_navigationFocusDisabled = disabled;
|
||||
emit navigationFocusDisabledChanged();
|
||||
}
|
||||
}
|
||||
|
||||
signals:
|
||||
void navigationFocusedChanged();
|
||||
void navigationFocusDisabledChanged();
|
||||
|
||||
private:
|
||||
bool _navigationFocused { false };
|
||||
bool _navigationFocusDisabled{ false };
|
||||
};
|
||||
|
||||
QString fixupHifiUrl(const QString& urlString) {
|
||||
|
@ -103,6 +116,10 @@ bool OffscreenUi::shouldSwallowShortcut(QEvent* event) {
|
|||
OffscreenUi::OffscreenUi() {
|
||||
}
|
||||
|
||||
QObject* OffscreenUi::getFlags() {
|
||||
return offscreenFlags;
|
||||
}
|
||||
|
||||
void OffscreenUi::create(QOpenGLContext* context) {
|
||||
OffscreenQmlSurface::create(context);
|
||||
auto rootContext = getRootContext();
|
||||
|
@ -392,7 +409,7 @@ QVariant OffscreenUi::waitForInputDialogResult(QQuickItem* inputDialog) {
|
|||
}
|
||||
|
||||
bool OffscreenUi::navigationFocused() {
|
||||
return offscreenFlags->isNavigationFocused();
|
||||
return !offscreenFlags->isNavigationFocusDisabled() && offscreenFlags->isNavigationFocused();
|
||||
}
|
||||
|
||||
void OffscreenUi::setNavigationFocused(bool focused) {
|
||||
|
|
|
@ -55,7 +55,7 @@ public:
|
|||
|
||||
bool eventFilter(QObject* originalDestination, QEvent* event) override;
|
||||
void addMenuInitializer(std::function<void(VrMenu*)> f);
|
||||
|
||||
QObject* getFlags();
|
||||
QQuickItem* getDesktop();
|
||||
QQuickItem* getToolWindow();
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <UserActivityLogger.h>
|
||||
#include <OffscreenUi.h>
|
||||
|
||||
|
||||
#include <controllers/UserInputMapper.h>
|
||||
|
||||
#include <controllers/StandardControls.h>
|
||||
|
@ -284,7 +285,6 @@ void ViveControllerManager::InputDevice::handleHandController(float deltaTime, u
|
|||
|
||||
vr::VRControllerState_t controllerState = vr::VRControllerState_t();
|
||||
if (_system->GetControllerState(deviceIndex, &controllerState)) {
|
||||
|
||||
// process each button
|
||||
for (uint32_t i = 0; i < vr::k_EButton_Max; ++i) {
|
||||
auto mask = vr::ButtonMaskFromId((vr::EVRButtonId)i);
|
||||
|
@ -342,6 +342,11 @@ void ViveControllerManager::InputDevice::handleAxisEvent(float deltaTime, uint32
|
|||
_axisStateMap[isLeftHand ? LY : RY] = stick.y;
|
||||
} else if (axis == vr::k_EButton_SteamVR_Trigger) {
|
||||
_axisStateMap[isLeftHand ? LT : RT] = x;
|
||||
// The click feeling on the Vive controller trigger represents a value of *precisely* 1.0,
|
||||
// so we can expose that as an additional button
|
||||
if (x >= 1.0f) {
|
||||
_buttonPressedMap.insert(isLeftHand ? LT_CLICK : RT_CLICK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -463,10 +468,15 @@ controller::Input::NamedVector ViveControllerManager::InputDevice::getAvailableI
|
|||
makePair(RS_X, "RSX"),
|
||||
makePair(RS_Y, "RSY"),
|
||||
|
||||
|
||||
// triggers
|
||||
makePair(LT, "LT"),
|
||||
makePair(RT, "RT"),
|
||||
|
||||
// Trigger clicks
|
||||
makePair(LT_CLICK, "LTClick"),
|
||||
makePair(RT_CLICK, "RTClick"),
|
||||
|
||||
// low profile side grip button.
|
||||
makePair(LEFT_GRIP, "LeftGrip"),
|
||||
makePair(RIGHT_GRIP, "RightGrip"),
|
||||
|
|
|
@ -29,9 +29,8 @@ var WANT_DEBUG_SEARCH_NAME = null;
|
|||
var SPARK_MODEL_SCALE_FACTOR = 0.75;
|
||||
|
||||
var TRIGGER_SMOOTH_RATIO = 0.1; // Time averaging of trigger - 0.0 disables smoothing
|
||||
var TRIGGER_ON_VALUE = 0.4; // Squeezed just enough to activate search or near grab
|
||||
var TRIGGER_GRAB_VALUE = 0.85; // Squeezed far enough to complete distant grab
|
||||
var TRIGGER_OFF_VALUE = 0.15;
|
||||
var TRIGGER_OFF_VALUE = 0.1;
|
||||
var TRIGGER_ON_VALUE = TRIGGER_OFF_VALUE + 0.05; // Squeezed just enough to activate search or near grab
|
||||
|
||||
var COLLIDE_WITH_AV_AFTER_RELEASE_DELAY = 0.25; // seconds
|
||||
|
||||
|
@ -393,6 +392,7 @@ function MyController(hand) {
|
|||
this.entityActivated = false;
|
||||
|
||||
this.triggerValue = 0; // rolling average of trigger value
|
||||
this.triggerClicked = false;
|
||||
this.rawTriggerValue = 0;
|
||||
this.rawSecondaryValue = 0;
|
||||
this.rawThumbValue = 0;
|
||||
|
@ -852,6 +852,10 @@ function MyController(hand) {
|
|||
_this.rawTriggerValue = value;
|
||||
};
|
||||
|
||||
this.triggerClick = function (value) {
|
||||
_this.triggerClicked = value;
|
||||
};
|
||||
|
||||
this.secondaryPress = function (value) {
|
||||
_this.rawSecondaryValue = value;
|
||||
};
|
||||
|
@ -864,7 +868,7 @@ function MyController(hand) {
|
|||
};
|
||||
|
||||
this.triggerSmoothedGrab = function () {
|
||||
return this.triggerValue > TRIGGER_GRAB_VALUE;
|
||||
return this.triggerClicked;
|
||||
};
|
||||
|
||||
this.triggerSmoothedSqueezed = function () {
|
||||
|
@ -2293,7 +2297,10 @@ var MAPPING_NAME = "com.highfidelity.handControllerGrab";
|
|||
|
||||
var mapping = Controller.newMapping(MAPPING_NAME);
|
||||
mapping.from([Controller.Standard.RT]).peek().to(rightController.triggerPress);
|
||||
mapping.from([Controller.Standard.RTClick]).peek().to(rightController.triggerClick);
|
||||
|
||||
mapping.from([Controller.Standard.LT]).peek().to(leftController.triggerPress);
|
||||
mapping.from([Controller.Standard.LTClick]).peek().to(leftController.triggerClick);
|
||||
|
||||
mapping.from([Controller.Standard.RB]).peek().to(rightController.secondaryPress);
|
||||
mapping.from([Controller.Standard.LB]).peek().to(leftController.secondaryPress);
|
||||
|
|
|
@ -49,23 +49,23 @@ function Trigger(label) {
|
|||
var that = this;
|
||||
that.label = label;
|
||||
that.TRIGGER_SMOOTH_RATIO = 0.1; // Time averaging of trigger - 0.0 disables smoothing
|
||||
that.TRIGGER_ON_VALUE = 0.4; // Squeezed just enough to activate search or near grab
|
||||
that.TRIGGER_GRAB_VALUE = 0.85; // Squeezed far enough to complete distant grab
|
||||
that.TRIGGER_OFF_VALUE = 0.15;
|
||||
that.TRIGGER_OFF_VALUE = 0.10;
|
||||
that.TRIGGER_ON_VALUE = that.TRIGGER_OFF_VALUE + 0.05; // Squeezed just enough to activate search or near grab
|
||||
that.rawTriggerValue = 0;
|
||||
that.triggerValue = 0; // rolling average of trigger value
|
||||
that.triggerPress = function (value) {
|
||||
that.rawTriggerValue = value;
|
||||
};
|
||||
that.triggerClicked = false;
|
||||
that.triggerClick = function (value) { that.triggerClicked = value; };
|
||||
that.triggerPress = function (value) { that.rawTriggerValue = value; };
|
||||
that.updateSmoothedTrigger = function () { // e.g., call once/update for effect
|
||||
var triggerValue = that.rawTriggerValue;
|
||||
// smooth out trigger value
|
||||
that.triggerValue = (that.triggerValue * that.TRIGGER_SMOOTH_RATIO) +
|
||||
(triggerValue * (1.0 - that.TRIGGER_SMOOTH_RATIO));
|
||||
OffscreenFlags.navigationFocusDisabled = that.triggerValue != 0.0;
|
||||
};
|
||||
// Current smoothed state, without hysteresis. Answering booleans.
|
||||
that.triggerSmoothedGrab = function () {
|
||||
return that.triggerValue > that.TRIGGER_GRAB_VALUE;
|
||||
that.triggerSmoothedClick = function () {
|
||||
return that.triggerClicked;
|
||||
};
|
||||
that.triggerSmoothedSqueezed = function () {
|
||||
return that.triggerValue > that.TRIGGER_ON_VALUE;
|
||||
|
@ -81,7 +81,7 @@ function Trigger(label) {
|
|||
that.updateSmoothedTrigger();
|
||||
|
||||
// The first two are independent of previous state:
|
||||
if (that.triggerSmoothedGrab()) {
|
||||
if (that.triggerSmoothedClick()) {
|
||||
state = 'full';
|
||||
} else if (that.triggerSmoothedReleased()) {
|
||||
state = null;
|
||||
|
@ -365,6 +365,8 @@ Script.scriptEnding.connect(clickMapping.disable);
|
|||
// Gather the trigger data for smoothing.
|
||||
clickMapping.from(Controller.Standard.RT).peek().to(rightTrigger.triggerPress);
|
||||
clickMapping.from(Controller.Standard.LT).peek().to(leftTrigger.triggerPress);
|
||||
clickMapping.from(Controller.Standard.RTClick).peek().to(rightTrigger.triggerClick);
|
||||
clickMapping.from(Controller.Standard.LTClick).peek().to(leftTrigger.triggerClick);
|
||||
// Full smoothed trigger is a click.
|
||||
function isPointingAtOverlayStartedNonFullTrigger(trigger) {
|
||||
// true if isPointingAtOverlay AND we were NOT full triggered when we became so.
|
||||
|
@ -493,7 +495,10 @@ function checkSettings() {
|
|||
updateRecommendedArea();
|
||||
}
|
||||
checkSettings();
|
||||
|
||||
var settingsChecker = Script.setInterval(checkSettings, SETTINGS_CHANGE_RECHECK_INTERVAL);
|
||||
Script.scriptEnding.connect(function () {
|
||||
Script.clearInterval(settingsChecker);
|
||||
OffscreenFlags.navigationFocusDisabled = false;
|
||||
});
|
||||
|
||||
|
|
87
scripts/system/libraries/Trigger.js
Normal file
87
scripts/system/libraries/Trigger.js
Normal file
|
@ -0,0 +1,87 @@
|
|||
"use strict";
|
||||
|
||||
/*jslint vars: true, plusplus: true*/
|
||||
/*globals Script, Overlays, Controller, Reticle, HMD, Camera, Entities, MyAvatar, Settings, Menu, ScriptDiscoveryService, Window, Vec3, Quat, print*/
|
||||
|
||||
Trigger = function(properties) {
|
||||
properties = properties || {};
|
||||
var that = this;
|
||||
that.label = properties.label || Math.random();
|
||||
that.SMOOTH_RATIO = properties.smooth || 0.1; // Time averaging of trigger - 0.0 disables smoothing
|
||||
that.DEADZONE = properties.deadzone || 0.10; // Once pressed, a trigger must fall below the deadzone to be considered un-pressed once pressed.
|
||||
that.HYSTERESIS = properties.hystersis || 0.05; // If not pressed, a trigger must go above DEADZONE + HYSTERSIS to be considered pressed
|
||||
|
||||
that.value = 0;
|
||||
that.pressed = false;
|
||||
that.clicked = false;
|
||||
|
||||
// Handlers
|
||||
that.onPress = properties.onPress || function(){
|
||||
print("Pressed trigger " + that.label)
|
||||
};
|
||||
that.onRelease = properties.onRelease || function(){
|
||||
print("Released trigger " + that.label)
|
||||
};
|
||||
that.onClick = properties.onClick || function(){
|
||||
print("Clicked trigger " + that.label)
|
||||
};
|
||||
that.onUnclick = properties.onUnclick || function(){
|
||||
print("Unclicked trigger " + that.label)
|
||||
};
|
||||
|
||||
// Getters
|
||||
that.isPressed = function() {
|
||||
return that.pressed;
|
||||
}
|
||||
|
||||
that.isClicked = function() {
|
||||
return that.clicked;
|
||||
}
|
||||
|
||||
that.getValue = function() {
|
||||
return that.value;
|
||||
}
|
||||
|
||||
|
||||
// Private values
|
||||
var controller = properties.controller || Controller.Standard.LT;
|
||||
var controllerClick = properties.controllerClick || Controller.Standard.LTClick;
|
||||
that.mapping = Controller.newMapping('com.highfidelity.controller.trigger.' + controller + '-' + controllerClick + '.' + that.label + Math.random());
|
||||
Script.scriptEnding.connect(that.mapping.disable);
|
||||
|
||||
// Setup mapping,
|
||||
that.mapping.from(controller).peek().to(function(value) {
|
||||
that.value = (that.value * that.SMOOTH_RATIO) +
|
||||
(value * (1.0 - that.SMOOTH_RATIO));
|
||||
|
||||
var oldPressed = that.pressed;
|
||||
if (!that.pressed && that.value >= (that.DEADZONE + that.HYSTERESIS)) {
|
||||
that.pressed = true;
|
||||
that.onPress();
|
||||
}
|
||||
|
||||
if (that.pressed && that.value < that.HYSTERESIS) {
|
||||
that.pressed = false;
|
||||
that.onRelease();
|
||||
}
|
||||
});
|
||||
|
||||
that.mapping.from(controllerClick).peek().to(function(value){
|
||||
if (!that.clicked && value > 0.0) {
|
||||
that.clicked = true;
|
||||
that.onClick();
|
||||
}
|
||||
if (that.clicked && value == 0.0) {
|
||||
that.clicked = false;
|
||||
that.onUnclick();
|
||||
}
|
||||
});
|
||||
|
||||
that.enable = function() {
|
||||
that.mapping.enable();
|
||||
}
|
||||
|
||||
that.disable = function() {
|
||||
that.mapping.disable();
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue