mirror of
https://github.com/overte-org/overte.git
synced 2025-04-07 17:52:26 +02:00
Make hand pointer lasers 'click' on fully pressing and clicking the vive controller
This commit is contained in:
parent
e18d664204
commit
091e798267
9 changed files with 156 additions and 13 deletions
|
@ -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" },
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -199,11 +199,19 @@ void OculusControllerManager::TouchDevice::update(float deltaTime, const control
|
|||
_axisStateMap[LX] = inputState.Thumbstick[ovrHand_Left].x;
|
||||
_axisStateMap[LY] = inputState.Thumbstick[ovrHand_Left].y;
|
||||
_axisStateMap[LT] = inputState.IndexTrigger[ovrHand_Left];
|
||||
// FIXME add hysteresis? Move to JSON config?
|
||||
if (inputState.IndexTrigger[ovrHand_Left] > 0.9) {
|
||||
_buttonPressedMap.insert(LT_CLICK);
|
||||
}
|
||||
_axisStateMap[LEFT_GRIP] = inputState.HandTrigger[ovrHand_Left];
|
||||
|
||||
_axisStateMap[RX] = inputState.Thumbstick[ovrHand_Right].x;
|
||||
_axisStateMap[RY] = inputState.Thumbstick[ovrHand_Right].y;
|
||||
_axisStateMap[RT] = inputState.IndexTrigger[ovrHand_Right];
|
||||
// FIXME add hysteresis? Move to JSON config?
|
||||
if (inputState.IndexTrigger[ovrHand_Right] > 0.9) {
|
||||
_buttonPressedMap.insert(RT_CLICK);
|
||||
}
|
||||
_axisStateMap[RIGHT_GRIP] = inputState.HandTrigger[ovrHand_Right];
|
||||
|
||||
// Buttons
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
|
||||
#include "ViveControllerManager.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
#include <PerfStat.h>
|
||||
#include <PathUtils.h>
|
||||
#include <GeometryCache.h>
|
||||
|
@ -22,6 +25,7 @@
|
|||
#include <UserActivityLogger.h>
|
||||
#include <OffscreenUi.h>
|
||||
|
||||
|
||||
#include <controllers/UserInputMapper.h>
|
||||
|
||||
#include <controllers/StandardControls.h>
|
||||
|
@ -284,7 +288,9 @@ void ViveControllerManager::InputDevice::handleHandController(float deltaTime, u
|
|||
|
||||
vr::VRControllerState_t controllerState = vr::VRControllerState_t();
|
||||
if (_system->GetControllerState(deviceIndex, &controllerState)) {
|
||||
|
||||
//std::stringstream stream;
|
||||
//stream << std::hex << controllerState.ulButtonPressed << " " << std::hex << controllerState.ulButtonTouched;
|
||||
//qDebug() << deviceIndex << " " << stream.str().c_str() << controllerState.rAxis[1].x;
|
||||
// process each button
|
||||
for (uint32_t i = 0; i < vr::k_EButton_Max; ++i) {
|
||||
auto mask = vr::ButtonMaskFromId((vr::EVRButtonId)i);
|
||||
|
@ -342,6 +348,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 +474,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 BUMPER_ON_VALUE = 0.5;
|
||||
|
||||
|
@ -376,6 +375,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;
|
||||
|
@ -835,6 +835,10 @@ function MyController(hand) {
|
|||
_this.rawTriggerValue = value;
|
||||
};
|
||||
|
||||
this.triggerClick = function (value) {
|
||||
_this.triggerClicked = value;
|
||||
};
|
||||
|
||||
this.secondaryPress = function (value) {
|
||||
_this.rawSecondaryValue = value;
|
||||
};
|
||||
|
@ -847,7 +851,7 @@ function MyController(hand) {
|
|||
};
|
||||
|
||||
this.triggerSmoothedGrab = function () {
|
||||
return this.triggerValue > TRIGGER_GRAB_VALUE;
|
||||
return this.triggerClicked;
|
||||
};
|
||||
|
||||
this.triggerSmoothedSqueezed = function () {
|
||||
|
@ -2225,7 +2229,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,11 +49,15 @@ 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.triggerClicked = false;
|
||||
that.triggerClick = function (value) {
|
||||
print("Trigger clicked is now " + value);
|
||||
that.triggerClicked = value;
|
||||
};
|
||||
that.triggerPress = function (value) {
|
||||
that.rawTriggerValue = value;
|
||||
};
|
||||
|
@ -64,8 +68,8 @@ function Trigger(label) {
|
|||
(triggerValue * (1.0 - that.TRIGGER_SMOOTH_RATIO));
|
||||
};
|
||||
// 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 +85,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 +369,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.
|
||||
|
|
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