mirror of
https://github.com/Armored-Dragon/overte.git
synced 2025-03-11 16:13:16 +01:00
Merge branch 'controllers' of https://github.com/highfidelity/hifi into controllers
This commit is contained in:
commit
09cdc01599
30 changed files with 925 additions and 590 deletions
|
@ -245,7 +245,7 @@ function MyController(hand, triggerAction) {
|
|||
};
|
||||
|
||||
this.updateSmoothedTrigger = function() {
|
||||
var triggerValue = Controller.getActionValue(this.triggerAction);
|
||||
var triggerValue = Controller.getValue(this.triggerAction);
|
||||
// smooth out trigger value
|
||||
this.triggerValue = (this.triggerValue * TRIGGER_SMOOTH_RATIO) +
|
||||
(triggerValue * (1.0 - TRIGGER_SMOOTH_RATIO));
|
||||
|
@ -260,7 +260,7 @@ function MyController(hand, triggerAction) {
|
|||
};
|
||||
|
||||
this.triggerSqueezed = function() {
|
||||
var triggerValue = Controller.getActionValue(this.triggerAction);
|
||||
var triggerValue = Controller.getValue(this.triggerAction);
|
||||
return triggerValue > TRIGGER_ON_VALUE;
|
||||
};
|
||||
|
||||
|
@ -402,8 +402,9 @@ function MyController(hand, triggerAction) {
|
|||
|
||||
this.distanceHolding = function() {
|
||||
|
||||
var handControllerPosition = Controller.getSpatialControlPosition(this.palm);
|
||||
var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(this.palm));
|
||||
var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition;
|
||||
var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand;
|
||||
var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(controllerHandInput).rotation);
|
||||
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position", "rotation",
|
||||
"gravity", "ignoreForCollisions"]);
|
||||
var now = Date.now();
|
||||
|
@ -452,8 +453,9 @@ function MyController(hand, triggerAction) {
|
|||
}
|
||||
|
||||
var handPosition = this.getHandPosition();
|
||||
var handControllerPosition = Controller.getSpatialControlPosition(this.palm);
|
||||
var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(this.palm));
|
||||
var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition;
|
||||
var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand;
|
||||
var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(controllerHandInput).rotation);
|
||||
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position", "rotation"]);
|
||||
|
||||
this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR);
|
||||
|
@ -595,7 +597,7 @@ function MyController(hand, triggerAction) {
|
|||
|
||||
}
|
||||
|
||||
this.currentHandControllerTipPosition = Controller.getSpatialControlPosition(this.tip);
|
||||
this.currentHandControllerTipPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandTipPosition : MyAvatar.leftHandTipPosition;;
|
||||
|
||||
this.currentObjectTime = Date.now();
|
||||
};
|
||||
|
@ -613,7 +615,7 @@ function MyController(hand, triggerAction) {
|
|||
// of it's actual offset, let's try imparting a velocity which is at a fixed radius
|
||||
// from the palm.
|
||||
|
||||
var handControllerPosition = Controller.getSpatialControlPosition(this.tip);
|
||||
var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition;
|
||||
var now = Date.now();
|
||||
|
||||
var deltaPosition = Vec3.subtract(handControllerPosition, this.currentHandControllerTipPosition); // meters
|
||||
|
@ -856,8 +858,8 @@ function MyController(hand, triggerAction) {
|
|||
};
|
||||
}
|
||||
|
||||
var rightController = new MyController(RIGHT_HAND, Controller.findAction("RIGHT_HAND_CLICK"));
|
||||
var leftController = new MyController(LEFT_HAND, Controller.findAction("LEFT_HAND_CLICK"));
|
||||
var rightController = new MyController(RIGHT_HAND, Controller.Standard.RT);
|
||||
var leftController = new MyController(LEFT_HAND, Controller.Standard.LT);
|
||||
|
||||
function update() {
|
||||
rightController.update();
|
||||
|
|
|
@ -1,4 +1,12 @@
|
|||
ControllerTest = function() {
|
||||
var standard = Controller.Standard;
|
||||
var actions = Controller.Actions;
|
||||
this.mappingEnabled = false;
|
||||
this.mapping = Controller.newMapping();
|
||||
this.mapping.from(standard.RX).to(actions.StepYaw);
|
||||
this.mapping.enable();
|
||||
this.mappingEnabled = true;
|
||||
|
||||
|
||||
print("Actions");
|
||||
for (var prop in Controller.Actions) {
|
||||
|
@ -24,6 +32,9 @@ ControllerTest = function() {
|
|||
}
|
||||
|
||||
ControllerTest.prototype.onCleanup = function() {
|
||||
if (this.mappingEnabled) {
|
||||
this.mapping.disable();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
{
|
||||
"name": "Hydra to Standard",
|
||||
"channels": [
|
||||
{ "from": "Hydra.LY", "to": "Standard.LY" },
|
||||
{ "from": "Hydra.LY", "filters": "invert", "to": "Standard.LY" },
|
||||
{ "from": "Hydra.LX", "to": "Standard.LX" },
|
||||
{ "from": "Hydra.LT", "to": "Standard.LT" },
|
||||
{ "from": "Hydra.RY", "to": "Standard.RY" },
|
||||
{ "from": "Hydra.RY", "filters": "invert", "to": "Standard.RY" },
|
||||
{ "from": "Hydra.RX", "to": "Standard.RX" },
|
||||
{ "from": "Hydra.RT", "to": "Standard.RT" },
|
||||
|
||||
|
|
43
interface/resources/controllers/standard-old.json
Normal file
43
interface/resources/controllers/standard-old.json
Normal file
|
@ -0,0 +1,43 @@
|
|||
{
|
||||
"name": "Standard to Action",
|
||||
"channels": [
|
||||
{ "from": "Standard.LY", "to": "Actions.TranslateZ" },
|
||||
{ "from": "Standard.LX", "to": "Actions.TranslateX" },
|
||||
{ "from": "Standard.RX", "to": "Actions.Yaw" },
|
||||
{ "from": "Standard.RY", "to": "Actions.Pitch" },
|
||||
{
|
||||
"from": "Standard.DU",
|
||||
"to": "Actions.LONGITUDINAL_FORWARD",
|
||||
"filters": [ { "type": "scale", "scale": 0.5 } ]
|
||||
},
|
||||
{
|
||||
"from": "Standard.DD",
|
||||
"to": "Actions.LONGITUDINAL_BACKWARD",
|
||||
"filters": [ { "type": "scale", "scale": 0.5 } ]
|
||||
},
|
||||
{
|
||||
"from": "Standard.DR",
|
||||
"to": "Actions.LATERAL_RIGHT",
|
||||
"filters": [ { "type": "scale", "scale": 0.5 } ]
|
||||
},
|
||||
{
|
||||
"from": "Standard.DL",
|
||||
"to": "Actions.LATERAL_LEFT",
|
||||
"filters": [ { "type": "scale", "scale": 0.5 } ]
|
||||
},
|
||||
{ "from": "Standard.Y", "to": "Actions.VERTICAL_UP" },
|
||||
{ "from": "Standard.X", "to": "Actions.VERTICAL_DOWN" },
|
||||
{
|
||||
"from": "Standard.RT",
|
||||
"to": "Actions.BOOM_IN",
|
||||
"filters": [ { "type": "scale", "scale": 0.1 } ]
|
||||
},
|
||||
{
|
||||
"from": "Standard.LT",
|
||||
"to": "Actions.BOOM_OUT",
|
||||
"filters": [ { "type": "scale", "scale": 0.1 } ]
|
||||
},
|
||||
{ "from": "Standard.LeftHand", "to": "Actions.LEFT_HAND" },
|
||||
{ "from": "Standard.RightHand", "to": "Actions.RIGHT_HAND" }
|
||||
]
|
||||
}
|
|
@ -5,39 +5,17 @@
|
|||
{ "from": "Standard.LX", "to": "Actions.TranslateX" },
|
||||
{ "from": "Standard.RX", "to": "Actions.Yaw" },
|
||||
{ "from": "Standard.RY", "to": "Actions.Pitch" },
|
||||
{
|
||||
"from": "Standard.DU",
|
||||
"to": "Actions.LONGITUDINAL_FORWARD",
|
||||
"filters": [ { "type": "scale", "scale": 0.5 } ]
|
||||
},
|
||||
{
|
||||
"from": "Standard.DD",
|
||||
"to": "Actions.LONGITUDINAL_BACKWARD",
|
||||
"filters": [ { "type": "scale", "scale": 0.5 } ]
|
||||
},
|
||||
{
|
||||
"from": "Standard.DR",
|
||||
"to": "Actions.LATERAL_RIGHT",
|
||||
"filters": [ { "type": "scale", "scale": 0.5 } ]
|
||||
},
|
||||
{
|
||||
"from": "Standard.DL",
|
||||
"to": "Actions.LATERAL_LEFT",
|
||||
"filters": [ { "type": "scale", "scale": 0.5 } ]
|
||||
},
|
||||
{ "from": "Standard.Y", "to": "Actions.VERTICAL_UP" },
|
||||
{ "from": "Standard.X", "to": "Actions.VERTICAL_DOWN" },
|
||||
{
|
||||
"from": "Standard.RT",
|
||||
"to": "Actions.BOOM_IN",
|
||||
"filters": [ { "type": "scale", "scale": 0.1 } ]
|
||||
},
|
||||
{
|
||||
"from": "Standard.LT",
|
||||
"to": "Actions.BOOM_OUT",
|
||||
"filters": [ { "type": "scale", "scale": 0.1 } ]
|
||||
},
|
||||
{ "from": "Standard.LeftHand", "to": "Actions.LEFT_HAND" },
|
||||
{ "from": "Standard.RightHand", "to": "Actions.RIGHT_HAND" }
|
||||
|
||||
{ "from": [ "Standard.DU", "Standard.DU", "Standard.DU", "Standard.DD" ], "to": "Standard.LeftPrimaryThumb" },
|
||||
{ "from": "Standard.Back", "to": "Standard.LeftSecondaryThumb" },
|
||||
|
||||
{ "from": [ "Standard.A", "Standard.B", "Standard.X", "Standard.Y" ], "to": "Standard.RightPrimaryThumb" },
|
||||
{ "from": "Standard.Start", "to": "Standard.RightSecondaryThumb" },
|
||||
|
||||
{ "from": "Standard.LT", "to": "Actions.LeftHandClick" },
|
||||
{ "from": "Standard.RT", "to": "Actions.RightHandClick" },
|
||||
|
||||
{ "from": "Standard.LeftHand", "to": "Actions.LeftHand" },
|
||||
{ "from": "Standard.RightHand", "to": "Actions.RightHand" }
|
||||
]
|
||||
}
|
||||
|
|
|
@ -3,12 +3,12 @@ import QtQuick.Controls 1.0
|
|||
import QtQuick.Layouts 1.0
|
||||
import QtQuick.Dialogs 1.0
|
||||
|
||||
Item {
|
||||
Rectangle {
|
||||
id: root
|
||||
property int size: 64
|
||||
width: size
|
||||
height: size
|
||||
|
||||
color: 'black'
|
||||
property int controlId: 0
|
||||
property real value: 0.5
|
||||
property int scrollWidth: 1
|
||||
|
@ -16,7 +16,7 @@ Item {
|
|||
property real max: 1.0
|
||||
property bool log: false
|
||||
property real range: max - min
|
||||
property color color: 'blue'
|
||||
property color lineColor: 'yellow'
|
||||
property bool bar: false
|
||||
property real lastHeight: -1
|
||||
property string label: ""
|
||||
|
@ -49,19 +49,21 @@ Item {
|
|||
Text {
|
||||
anchors.top: parent.top
|
||||
text: root.label
|
||||
|
||||
color: 'white'
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
text: root.max
|
||||
color: 'white'
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
text: root.min
|
||||
color: 'white'
|
||||
}
|
||||
|
||||
function scroll() {
|
||||
|
@ -92,7 +94,7 @@ Item {
|
|||
|
||||
ctx.beginPath();
|
||||
ctx.lineWidth = 1
|
||||
ctx.strokeStyle = root.color
|
||||
ctx.strokeStyle = root.lineColor
|
||||
ctx.moveTo(canvas.width - root.scrollWidth, root.lastHeight).lineTo(canvas.width, currentHeight)
|
||||
ctx.stroke()
|
||||
ctx.restore()
|
||||
|
|
|
@ -20,8 +20,23 @@ HifiControls.VrDialog {
|
|||
property var standard: Controller.Standard
|
||||
property var hydra: null
|
||||
property var testMapping: null
|
||||
property bool testMappingEnabled: false
|
||||
property var xbox: null
|
||||
|
||||
function buildMapping() {
|
||||
testMapping = Controller.newMapping();
|
||||
testMapping.from(standard.RY).invert().to(actions.Pitch);
|
||||
//testMapping.makeAxis(standard.LB, standard.RB).to(actions.Yaw);
|
||||
// Step yaw takes a number of degrees
|
||||
testMapping.from(standard.LB).invert().scale(15.0).to(actions.StepYaw);
|
||||
testMapping.from(standard.RB).scale(15.0).to(actions.StepYaw);
|
||||
testMapping.from(standard.RX).scale(15.0).to(actions.StepYaw);
|
||||
}
|
||||
|
||||
function toggleMapping() {
|
||||
testMapping.enable(!testMappingEnabled);
|
||||
testMappingEnabled = !testMappingEnabled;
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
enabled = true
|
||||
|
@ -49,166 +64,63 @@ HifiControls.VrDialog {
|
|||
|
||||
Row {
|
||||
spacing: 8
|
||||
Button {
|
||||
text: "Standard Mapping"
|
||||
onClicked: {
|
||||
var mapping = Controller.newMapping("Default");
|
||||
mapping.from(standard.LX).to(actions.TranslateX);
|
||||
mapping.from(standard.LY).to(actions.TranslateZ);
|
||||
mapping.from(standard.RY).to(actions.Pitch);
|
||||
mapping.from(standard.RX).to(actions.Yaw);
|
||||
mapping.from(standard.DU).scale(0.5).to(actions.LONGITUDINAL_FORWARD);
|
||||
mapping.from(standard.DD).scale(0.5).to(actions.LONGITUDINAL_BACKWARD);
|
||||
mapping.from(standard.DL).scale(0.5).to(actions.LATERAL_LEFT);
|
||||
mapping.from(standard.DR).scale(0.5).to(actions.LATERAL_RIGHT);
|
||||
mapping.from(standard.X).to(actions.VERTICAL_DOWN);
|
||||
mapping.from(standard.Y).to(actions.VERTICAL_UP);
|
||||
mapping.from(standard.RT).scale(0.1).to(actions.BOOM_IN);
|
||||
mapping.from(standard.LT).scale(0.1).to(actions.BOOM_OUT);
|
||||
mapping.from(standard.B).to(actions.ACTION1);
|
||||
mapping.from(standard.A).to(actions.ACTION2);
|
||||
mapping.from(standard.RB).to(actions.SHIFT);
|
||||
mapping.from(standard.Back).to(actions.TOGGLE_MUTE);
|
||||
mapping.from(standard.Start).to(actions.CONTEXT_MENU);
|
||||
Controller.enableMapping("Default");
|
||||
enabled = false;
|
||||
text = "Standard Built"
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
text: root.xbox ? "XBox Mapping" : "XBox not found"
|
||||
property bool built: false
|
||||
enabled: root.xbox && !built
|
||||
text: !root.testMapping ? "Build Mapping" : (root.testMappingEnabled ? "Disable Mapping" : "Enable Mapping")
|
||||
onClicked: {
|
||||
var mapping = Controller.newMapping();
|
||||
mapping.from(xbox.A).to(standard.A);
|
||||
mapping.from(xbox.B).to(standard.B);
|
||||
mapping.from(xbox.X).to(standard.X);
|
||||
mapping.from(xbox.Y).to(standard.Y);
|
||||
mapping.from(xbox.Up).to(standard.DU);
|
||||
mapping.from(xbox.Down).to(standard.DD);
|
||||
mapping.from(xbox.Left).to(standard.DL);
|
||||
mapping.from(xbox.Right).to(standard.Right);
|
||||
mapping.from(xbox.LB).to(standard.LB);
|
||||
mapping.from(xbox.RB).to(standard.RB);
|
||||
mapping.from(xbox.LS).to(standard.LS);
|
||||
mapping.from(xbox.RS).to(standard.RS);
|
||||
mapping.from(xbox.Start).to(standard.Start);
|
||||
mapping.from(xbox.Back).to(standard.Back);
|
||||
mapping.from(xbox.LY).to(standard.LY);
|
||||
mapping.from(xbox.LX).to(standard.LX);
|
||||
mapping.from(xbox.RY).to(standard.RY);
|
||||
mapping.from(xbox.RX).to(standard.RX);
|
||||
mapping.from(xbox.LT).to(standard.LT);
|
||||
mapping.from(xbox.RT).to(standard.RT);
|
||||
mapping.enable();
|
||||
built = false;
|
||||
text = "XBox Built"
|
||||
|
||||
if (!root.testMapping) {
|
||||
root.buildMapping()
|
||||
} else {
|
||||
root.toggleMapping();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
text: root.hydra ? "Hydra Mapping" : "Hydra Not Found"
|
||||
property bool built: false
|
||||
enabled: root.hydra && !built
|
||||
onClicked: {
|
||||
var mapping = Controller.newMapping();
|
||||
mapping.from(hydra.LY).invert().to(standard.LY);
|
||||
mapping.from(hydra.LX).to(standard.LX);
|
||||
mapping.from(hydra.RY).invert().to(standard.RY);
|
||||
mapping.from(hydra.RX).to(standard.RX);
|
||||
mapping.from(hydra.LT).to(standard.LT);
|
||||
mapping.from(hydra.RT).to(standard.RT);
|
||||
mapping.enable();
|
||||
built = false;
|
||||
text = "Hydra Built"
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "Test Mapping"
|
||||
onClicked: {
|
||||
var mapping = Controller.newMapping();
|
||||
// Inverting a value
|
||||
mapping.from(hydra.RY).invert().to(standard.RY);
|
||||
mapping.from(hydra.RX).to(standard.RX);
|
||||
mapping.from(hydra.LY).to(standard.LY);
|
||||
mapping.from(hydra.LX).to(standard.LX);
|
||||
// Assigning a value from a function
|
||||
// mapping.from(function() { return Math.sin(Date.now() / 250); }).to(standard.RX);
|
||||
// Constrainting a value to -1, 0, or 1, with a deadzone
|
||||
// mapping.from(xbox.LY).deadZone(0.5).constrainToInteger().to(standard.LY);
|
||||
mapping.makeAxis(standard.LB, standard.RB).to(actions.Yaw);
|
||||
// mapping.from(actions.Yaw).clamp(0, 1).invert().to(actions.YAW_RIGHT);
|
||||
// mapping.from(actions.Yaw).clamp(-1, 0).to(actions.YAW_LEFT);
|
||||
// mapping.modifier(keyboard.Ctrl).scale(2.0)
|
||||
// mapping.from(keyboard.A).to(actions.TranslateLeft)
|
||||
// mapping.from(keyboard.A, keyboard.Shift).to(actions.TurnLeft)
|
||||
// mapping.from(keyboard.A, keyboard.Shift, keyboard.Ctrl).scale(2.0).to(actions.TurnLeft)
|
||||
// // First loopbacks
|
||||
// // Then non-loopbacks by constraint level (number of inputs)
|
||||
// mapping.from(xbox.RX).deadZone(0.2).to(xbox.RX)
|
||||
// mapping.from(standard.RB, standard.LB, keyboard.Shift).to(actions.TurnLeft)
|
||||
// mapping.from(keyboard.A, keyboard.Shift).to(actions.TurnLeft)
|
||||
// mapping.from(keyboard.W).when(keyboard.Shift).to(actions.Forward)
|
||||
testMapping = mapping;
|
||||
enabled = false
|
||||
text = "Built"
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "Enable Mapping"
|
||||
onClicked: root.testMapping.enable()
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "Disable Mapping"
|
||||
onClicked: root.testMapping.disable()
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "Enable Mapping"
|
||||
onClicked: print(Controller.getValue(root.xbox.LY));
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
Xbox { device: root.standard; label: "Standard"; width: 360 }
|
||||
Standard { device: root.standard; label: "Standard"; width: 180 }
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing: 8
|
||||
Xbox { device: root.xbox; label: "XBox"; width: 360 }
|
||||
Hydra { device: root.hydra; width: 360 }
|
||||
Xbox { device: root.xbox; label: "XBox"; width: 180 }
|
||||
Hydra { device: root.hydra; width: 180 }
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing: 8
|
||||
ScrollingGraph {
|
||||
controlId: Controller.Actions.Yaw
|
||||
label: "Yaw"
|
||||
min: -2.0
|
||||
max: 2.0
|
||||
size: 64
|
||||
}
|
||||
|
||||
ScrollingGraph {
|
||||
controlId: Controller.Actions.YawLeft
|
||||
label: "Yaw Left"
|
||||
min: -2.0
|
||||
max: 2.0
|
||||
size: 64
|
||||
}
|
||||
|
||||
ScrollingGraph {
|
||||
controlId: Controller.Actions.YawRight
|
||||
label: "Yaw Right"
|
||||
min: -2.0
|
||||
max: 2.0
|
||||
size: 64
|
||||
}
|
||||
ScrollingGraph {
|
||||
controlId: Controller.Actions.StepYaw
|
||||
label: "StepYaw"
|
||||
min: -2.0
|
||||
max: 2.0
|
||||
size: 64
|
||||
}
|
||||
}
|
||||
// Row {
|
||||
// spacing: 8
|
||||
// ScrollingGraph {
|
||||
// controlId: Controller.Actions.Yaw
|
||||
// label: "Yaw"
|
||||
// min: -3.0
|
||||
// max: 3.0
|
||||
// size: 128
|
||||
// }
|
||||
//
|
||||
// ScrollingGraph {
|
||||
// controlId: Controller.Actions.YAW_LEFT
|
||||
// label: "Yaw Left"
|
||||
// min: -3.0
|
||||
// max: 3.0
|
||||
// size: 128
|
||||
// }
|
||||
//
|
||||
// ScrollingGraph {
|
||||
// controlId: Controller.Actions.YAW_RIGHT
|
||||
// label: "Yaw Right"
|
||||
// min: -3.0
|
||||
// max: 3.0
|
||||
// size: 128
|
||||
// }
|
||||
// }
|
||||
}
|
||||
} // dialog
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ Item {
|
|||
property color color: 'black'
|
||||
|
||||
function update() {
|
||||
value = Controller.getValue(controlId);
|
||||
value = controlId ? Controller.getValue(controlId) : 0;
|
||||
canvas.requestPaint();
|
||||
}
|
||||
|
||||
|
|
|
@ -17,8 +17,8 @@ Item {
|
|||
|
||||
function update() {
|
||||
value = Qt.vector2d(
|
||||
Controller.getValue(controlIds[0]),
|
||||
Controller.getValue(controlIds[1])
|
||||
controlIds[0] ? Controller.getValue(controlIds[0]) : 0,
|
||||
controlIds[1] ? Controller.getValue(controlIds[1]) : 0
|
||||
);
|
||||
if (root.invertY) {
|
||||
value.y = value.y * -1.0
|
||||
|
@ -27,7 +27,7 @@ Item {
|
|||
}
|
||||
|
||||
Timer {
|
||||
interval: 50; running: true; repeat: true
|
||||
interval: 50; running: controlIds; repeat: true
|
||||
onTriggered: root.update()
|
||||
}
|
||||
|
||||
|
|
35
interface/resources/qml/controller/Standard.qml
Normal file
35
interface/resources/qml/controller/Standard.qml
Normal file
|
@ -0,0 +1,35 @@
|
|||
import QtQuick 2.1
|
||||
import QtQuick.Controls 1.0
|
||||
import QtQuick.Layouts 1.0
|
||||
import QtQuick.Dialogs 1.0
|
||||
|
||||
import "xbox"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property real aspect: 300.0 / 215.0
|
||||
width: 300
|
||||
height: width / aspect
|
||||
property var device
|
||||
property string label: ""
|
||||
property real scale: width / 300.0
|
||||
|
||||
Xbox {
|
||||
width: root.width; height: root.height
|
||||
device: root.device
|
||||
}
|
||||
|
||||
// Left primary
|
||||
ToggleButton {
|
||||
x: 0; y: parent.height - height;
|
||||
controlId: root.device.LeftPrimaryThumb
|
||||
width: 16 * root.scale; height: 16 * root.scale
|
||||
}
|
||||
|
||||
// Left primary
|
||||
ToggleButton {
|
||||
x: parent.width - width; y: parent.height - height;
|
||||
controlId: root.device.RightPrimaryThumb
|
||||
width: 16 * root.scale; height: 16 * root.scale
|
||||
}
|
||||
}
|
|
@ -12,12 +12,14 @@ Item {
|
|||
property real value: 0
|
||||
property color color: 'black'
|
||||
|
||||
function update() {
|
||||
value = controlId ? Controller.getValue(controlId) : 0;
|
||||
canvas.requestPaint();
|
||||
}
|
||||
|
||||
Timer {
|
||||
interval: 50; running: true; repeat: true
|
||||
onTriggered: {
|
||||
root.value = Controller.getValue(root.controlId);
|
||||
canvas.requestPaint();
|
||||
}
|
||||
interval: 50; running: root.controlId; repeat: true
|
||||
onTriggered: root.update()
|
||||
}
|
||||
|
||||
Canvas {
|
||||
|
|
|
@ -2718,15 +2718,13 @@ void Application::update(float deltaTime) {
|
|||
}
|
||||
|
||||
// Transfer the user inputs to the driveKeys
|
||||
// FIXME can we drop drive keys and just have the avatar read the action states directly?
|
||||
myAvatar->clearDriveKeys();
|
||||
if (_myCamera.getMode() != CAMERA_MODE_INDEPENDENT) {
|
||||
if (!_controllerScriptingInterface->areActionsCaptured()) {
|
||||
myAvatar->setDriveKeys(FWD, userInputMapper->getActionState(controller::Action::LONGITUDINAL_FORWARD));
|
||||
myAvatar->setDriveKeys(BACK, userInputMapper->getActionState(controller::Action::LONGITUDINAL_BACKWARD));
|
||||
myAvatar->setDriveKeys(UP, userInputMapper->getActionState(controller::Action::VERTICAL_UP));
|
||||
myAvatar->setDriveKeys(DOWN, userInputMapper->getActionState(controller::Action::VERTICAL_DOWN));
|
||||
myAvatar->setDriveKeys(LEFT, userInputMapper->getActionState(controller::Action::LATERAL_LEFT));
|
||||
myAvatar->setDriveKeys(RIGHT, userInputMapper->getActionState(controller::Action::LATERAL_RIGHT));
|
||||
myAvatar->setDriveKeys(TRANSLATE_Z, -1.0f * userInputMapper->getActionState(controller::Action::TRANSLATE_Z));
|
||||
myAvatar->setDriveKeys(TRANSLATE_Y, userInputMapper->getActionState(controller::Action::TRANSLATE_Y));
|
||||
myAvatar->setDriveKeys(TRANSLATE_X, userInputMapper->getActionState(controller::Action::TRANSLATE_X));
|
||||
if (deltaTime > FLT_EPSILON) {
|
||||
// For rotations what we really want are meausures of "angles per second" (in order to prevent
|
||||
// fps-dependent spin rates) so we need to scale the units of the controller contribution.
|
||||
|
@ -2734,14 +2732,12 @@ void Application::update(float deltaTime) {
|
|||
// controllers to provide a delta_per_second value rather than a raw delta.)
|
||||
const float EXPECTED_FRAME_RATE = 60.0f;
|
||||
float timeFactor = EXPECTED_FRAME_RATE * deltaTime;
|
||||
myAvatar->setDriveKeys(ROT_UP, userInputMapper->getActionState(controller::Action::PITCH_UP) / timeFactor);
|
||||
myAvatar->setDriveKeys(ROT_DOWN, userInputMapper->getActionState(controller::Action::PITCH_DOWN) / timeFactor);
|
||||
myAvatar->setDriveKeys(ROT_LEFT, userInputMapper->getActionState(controller::Action::YAW_LEFT) / timeFactor);
|
||||
myAvatar->setDriveKeys(ROT_RIGHT, userInputMapper->getActionState(controller::Action::YAW_RIGHT) / timeFactor);
|
||||
myAvatar->setDriveKeys(PITCH, -1.0f * userInputMapper->getActionState(controller::Action::PITCH) / timeFactor);
|
||||
myAvatar->setDriveKeys(YAW, -1.0f * userInputMapper->getActionState(controller::Action::YAW) / timeFactor);
|
||||
myAvatar->setDriveKeys(STEP_YAW, -1.0f * userInputMapper->getActionState(controller::Action::STEP_YAW) / timeFactor);
|
||||
}
|
||||
}
|
||||
myAvatar->setDriveKeys(BOOM_IN, userInputMapper->getActionState(controller::Action::BOOM_IN));
|
||||
myAvatar->setDriveKeys(BOOM_OUT, userInputMapper->getActionState(controller::Action::BOOM_OUT));
|
||||
myAvatar->setDriveKeys(ZOOM, userInputMapper->getActionState(controller::Action::TRANSLATE_CAMERA_Z));
|
||||
}
|
||||
controller::Pose leftHand = userInputMapper->getPoseState(controller::Action::LEFT_HAND);
|
||||
controller::Pose rightHand = userInputMapper->getPoseState(controller::Action::RIGHT_HAND);
|
||||
|
|
|
@ -43,21 +43,6 @@ static const float BILLBOARD_DISTANCE = 5.56f; // meters
|
|||
extern const float CHAT_MESSAGE_SCALE;
|
||||
extern const float CHAT_MESSAGE_HEIGHT;
|
||||
|
||||
enum DriveKeys {
|
||||
FWD = 0,
|
||||
BACK,
|
||||
LEFT,
|
||||
RIGHT,
|
||||
UP,
|
||||
DOWN,
|
||||
ROT_LEFT,
|
||||
ROT_RIGHT,
|
||||
ROT_UP,
|
||||
ROT_DOWN,
|
||||
BOOM_IN,
|
||||
BOOM_OUT,
|
||||
MAX_DRIVE_KEYS
|
||||
};
|
||||
|
||||
enum ScreenTintLayer {
|
||||
SCREEN_TINT_BEFORE_LANDSCAPE = 0,
|
||||
|
|
|
@ -53,6 +53,7 @@
|
|||
|
||||
using namespace std;
|
||||
|
||||
static quint64 COMFORT_MODE_PULSE_TIMING = USECS_PER_SECOND / 2; // turn once per half second
|
||||
const glm::vec3 DEFAULT_UP_DIRECTION(0.0f, 1.0f, 0.0f);
|
||||
const float YAW_SPEED = 150.0f; // degrees/sec
|
||||
const float PITCH_SPEED = 100.0f; // degrees/sec
|
||||
|
@ -240,8 +241,31 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
|
||||
{
|
||||
PerformanceTimer perfTimer("transform");
|
||||
bool stepAction = false;
|
||||
// When there are no step values, we zero out the last step pulse.
|
||||
// This allows a user to do faster snapping by tapping a control
|
||||
for (int i = STEP_TRANSLATE_X; !stepAction && i <= STEP_YAW; ++i) {
|
||||
if (_driveKeys[i] != 0.0f) {
|
||||
stepAction = true;
|
||||
}
|
||||
}
|
||||
quint64 now = usecTimestampNow();
|
||||
quint64 pulseDeltaTime = now - _lastStepPulse;
|
||||
if (!stepAction) {
|
||||
_lastStepPulse = 0;
|
||||
}
|
||||
|
||||
if (stepAction && pulseDeltaTime > COMFORT_MODE_PULSE_TIMING) {
|
||||
_pulseUpdate = true;
|
||||
}
|
||||
|
||||
updateOrientation(deltaTime);
|
||||
updatePosition(deltaTime);
|
||||
|
||||
if (_pulseUpdate) {
|
||||
_lastStepPulse = now;
|
||||
_pulseUpdate = false;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -524,6 +548,54 @@ void MyAvatar::updateFromTrackers(float deltaTime) {
|
|||
}
|
||||
|
||||
|
||||
// FIXME - this is super duper dumb... but this is how master works. When you have
|
||||
// hydras plugged in, you'll get 4 "palms" but only the number of controllers lifted
|
||||
// of the base station are considered active. So when you ask for "left" you get the
|
||||
// first active controller. If you have both controllers held up or just the left, that
|
||||
// will be correct. But if you lift the right controller, then it will be reported
|
||||
// as "left"... you also see this in the avatars hands.
|
||||
const PalmData* MyAvatar::getActivePalm(int palmIndex) const {
|
||||
const HandData* handData = DependencyManager::get<AvatarManager>()->getMyAvatar()->getHandData();
|
||||
int numberOfPalms = handData->getNumPalms();
|
||||
int numberOfActivePalms = 0;
|
||||
for (int i = 0; i < numberOfPalms; i++) {
|
||||
auto palm = handData->getPalms()[i];
|
||||
if (palm.isActive()) {
|
||||
// if we've reached the requested "active" palm, then we will return it
|
||||
if (numberOfActivePalms == palmIndex) {
|
||||
return &handData->getPalms()[i];
|
||||
}
|
||||
numberOfActivePalms++;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
glm::vec3 MyAvatar::getLeftHandPosition() const {
|
||||
const int LEFT_HAND = 0;
|
||||
auto palmData = getActivePalm(LEFT_HAND);
|
||||
return palmData ? palmData->getPosition() : glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
glm::vec3 MyAvatar::getRightHandPosition() const {
|
||||
const int RIGHT_HAND = 1;
|
||||
auto palmData = getActivePalm(RIGHT_HAND);
|
||||
return palmData ? palmData->getPosition() : glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
glm::vec3 MyAvatar::getLeftHandTipPosition() const {
|
||||
const int LEFT_HAND = 0;
|
||||
auto palmData = getActivePalm(LEFT_HAND);
|
||||
return palmData ? palmData->getTipPosition() : glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
glm::vec3 MyAvatar::getRightHandTipPosition() const {
|
||||
const int RIGHT_HAND = 1;
|
||||
auto palmData = getActivePalm(RIGHT_HAND);
|
||||
return palmData ? palmData->getTipPosition() : glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
// virtual
|
||||
void MyAvatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
|
||||
// don't render if we've been asked to disable local rendering
|
||||
|
@ -1506,69 +1578,44 @@ bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs) const {
|
|||
|
||||
void MyAvatar::updateOrientation(float deltaTime) {
|
||||
// Smoothly rotate body with arrow keys
|
||||
float targetSpeed = 0.0f;
|
||||
|
||||
// FIXME - this comfort mode code is a total hack, remove it when we have new input mapping
|
||||
bool isComfortMode = Menu::getInstance()->isOptionChecked(MenuOption::ComfortMode);
|
||||
bool isHMDMode = qApp->getAvatarUpdater()->isHMDMode();
|
||||
|
||||
if (!isHMDMode || !isComfortMode) {
|
||||
targetSpeed = (_driveKeys[ROT_LEFT] - _driveKeys[ROT_RIGHT]) * YAW_SPEED;
|
||||
|
||||
if (targetSpeed != 0.0f) {
|
||||
const float ROTATION_RAMP_TIMESCALE = 0.1f;
|
||||
float blend = deltaTime / ROTATION_RAMP_TIMESCALE;
|
||||
if (blend > 1.0f) {
|
||||
blend = 1.0f;
|
||||
}
|
||||
_bodyYawDelta = (1.0f - blend) * _bodyYawDelta + blend * targetSpeed;
|
||||
} else if (_bodyYawDelta != 0.0f) {
|
||||
// attenuate body rotation speed
|
||||
const float ROTATION_DECAY_TIMESCALE = 0.05f;
|
||||
float attenuation = 1.0f - deltaTime / ROTATION_DECAY_TIMESCALE;
|
||||
if (attenuation < 0.0f) {
|
||||
attenuation = 0.0f;
|
||||
}
|
||||
_bodyYawDelta *= attenuation;
|
||||
|
||||
float MINIMUM_ROTATION_RATE = 2.0f;
|
||||
if (fabsf(_bodyYawDelta) < MINIMUM_ROTATION_RATE) {
|
||||
_bodyYawDelta = 0.0f;
|
||||
}
|
||||
float targetSpeed = _driveKeys[YAW] * YAW_SPEED;
|
||||
if (targetSpeed != 0.0f) {
|
||||
const float ROTATION_RAMP_TIMESCALE = 0.1f;
|
||||
float blend = deltaTime / ROTATION_RAMP_TIMESCALE;
|
||||
if (blend > 1.0f) {
|
||||
blend = 1.0f;
|
||||
}
|
||||
_bodyYawDelta = (1.0f - blend) * _bodyYawDelta + blend * targetSpeed;
|
||||
} else if (_bodyYawDelta != 0.0f) {
|
||||
// attenuate body rotation speed
|
||||
const float ROTATION_DECAY_TIMESCALE = 0.05f;
|
||||
float attenuation = 1.0f - deltaTime / ROTATION_DECAY_TIMESCALE;
|
||||
if (attenuation < 0.0f) {
|
||||
attenuation = 0.0f;
|
||||
}
|
||||
_bodyYawDelta *= attenuation;
|
||||
|
||||
// update body orientation by movement inputs
|
||||
setOrientation(getOrientation() *
|
||||
glm::quat(glm::radians(glm::vec3(0.0f, _bodyYawDelta * deltaTime, 0.0f))));
|
||||
|
||||
} else {
|
||||
// Comfort Mode: If you press any of the left/right rotation drive keys or input, you'll
|
||||
// get an instantaneous 15 degree turn. If you keep holding the key down you'll get another
|
||||
// snap turn every half second.
|
||||
_bodyYawDelta = 0.0f;
|
||||
|
||||
static quint64 lastPulse = 0;
|
||||
quint64 now = usecTimestampNow();
|
||||
quint64 COMFORT_MODE_PULSE_TIMING = USECS_PER_SECOND / 2; // turn once per half second
|
||||
|
||||
float driveLeft = _driveKeys[ROT_LEFT];
|
||||
float driveRight= _driveKeys[ROT_RIGHT];
|
||||
|
||||
if ((driveLeft != 0.0f || driveRight != 0.0f) && (now - lastPulse > COMFORT_MODE_PULSE_TIMING)) {
|
||||
lastPulse = now;
|
||||
|
||||
const float SNAP_TURN_DELTA = 15.0f; // degrees
|
||||
float direction = (driveLeft - driveRight) < 0.0f ? -1.0f : 1.0f;
|
||||
float turnAmount = direction * SNAP_TURN_DELTA;
|
||||
|
||||
// update body orientation by movement inputs
|
||||
setOrientation(getOrientation() *
|
||||
glm::quat(glm::radians(glm::vec3(0.0f, turnAmount, 0.0f))));
|
||||
|
||||
float MINIMUM_ROTATION_RATE = 2.0f;
|
||||
if (fabsf(_bodyYawDelta) < MINIMUM_ROTATION_RATE) {
|
||||
_bodyYawDelta = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
getHead()->setBasePitch(getHead()->getBasePitch() + (_driveKeys[ROT_UP] - _driveKeys[ROT_DOWN]) * PITCH_SPEED * deltaTime);
|
||||
float totalBodyYaw = _bodyYawDelta * deltaTime;
|
||||
|
||||
|
||||
// Comfort Mode: If you press any of the left/right rotation drive keys or input, you'll
|
||||
// get an instantaneous 15 degree turn. If you keep holding the key down you'll get another
|
||||
// snap turn every half second.
|
||||
quint64 now = usecTimestampNow();
|
||||
if (_driveKeys[STEP_YAW] != 0.0f && now - _lastStepPulse > COMFORT_MODE_PULSE_TIMING) {
|
||||
totalBodyYaw += _driveKeys[STEP_YAW];
|
||||
}
|
||||
|
||||
// update body orientation by movement inputs
|
||||
setOrientation(getOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, totalBodyYaw, 0.0f))));
|
||||
|
||||
getHead()->setBasePitch(getHead()->getBasePitch() + _driveKeys[PITCH] * PITCH_SPEED * deltaTime);
|
||||
|
||||
if (qApp->getAvatarUpdater()->isHMDMode()) {
|
||||
glm::quat orientation = glm::quat_cast(getSensorToWorldMatrix()) * getHMDSensorOrientation();
|
||||
|
@ -1628,14 +1675,18 @@ glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVe
|
|||
float motorEfficiency = glm::clamp(deltaTime / timescale, 0.0f, 1.0f);
|
||||
|
||||
glm::vec3 newLocalVelocity = localVelocity;
|
||||
float keyboardInput = fabsf(_driveKeys[FWD] - _driveKeys[BACK]) +
|
||||
(fabsf(_driveKeys[RIGHT] - _driveKeys[LEFT])) +
|
||||
fabsf(_driveKeys[UP] - _driveKeys[DOWN]);
|
||||
float stepControllerInput = fabsf(_driveKeys[STEP_TRANSLATE_Z]) + fabsf(_driveKeys[STEP_TRANSLATE_Z]) + fabsf(_driveKeys[STEP_TRANSLATE_Z]);
|
||||
quint64 now = usecTimestampNow();
|
||||
// FIXME how do I implement step translation as well?
|
||||
if (stepControllerInput && now - _lastStepPulse > COMFORT_MODE_PULSE_TIMING) {
|
||||
}
|
||||
|
||||
float keyboardInput = fabsf(_driveKeys[TRANSLATE_Z]) + fabsf(_driveKeys[TRANSLATE_X]) + fabsf(_driveKeys[TRANSLATE_Y]);
|
||||
if (keyboardInput) {
|
||||
// Compute keyboard input
|
||||
glm::vec3 front = (_driveKeys[FWD] - _driveKeys[BACK]) * IDENTITY_FRONT;
|
||||
glm::vec3 right = (_driveKeys[RIGHT] - _driveKeys[LEFT]) * IDENTITY_RIGHT;
|
||||
glm::vec3 up = (_driveKeys[UP] - _driveKeys[DOWN]) * IDENTITY_UP;
|
||||
glm::vec3 front = (_driveKeys[TRANSLATE_Z]) * IDENTITY_FRONT;
|
||||
glm::vec3 right = (_driveKeys[TRANSLATE_X]) * IDENTITY_RIGHT;
|
||||
glm::vec3 up = (_driveKeys[TRANSLATE_Y]) * IDENTITY_UP;
|
||||
|
||||
glm::vec3 direction = front + right + up;
|
||||
float directionLength = glm::length(direction);
|
||||
|
@ -1686,7 +1737,7 @@ glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVe
|
|||
}
|
||||
}
|
||||
|
||||
float boomChange = _driveKeys[BOOM_OUT] - _driveKeys[BOOM_IN];
|
||||
float boomChange = _driveKeys[ZOOM];
|
||||
_boomLength += 2.0f * _boomLength * boomChange + boomChange * boomChange;
|
||||
_boomLength = glm::clamp<float>(_boomLength, ZOOM_MIN, ZOOM_MAX);
|
||||
|
||||
|
@ -1935,7 +1986,7 @@ void MyAvatar::clearDriveKeys() {
|
|||
}
|
||||
|
||||
void MyAvatar::relayDriveKeysToCharacterController() {
|
||||
if (_driveKeys[UP] > 0.0f) {
|
||||
if (_driveKeys[TRANSLATE_Y] > 0.0f) {
|
||||
_characterController.jump();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,20 @@
|
|||
|
||||
class ModelItemID;
|
||||
|
||||
enum DriveKeys {
|
||||
TRANSLATE_X = 0,
|
||||
TRANSLATE_Y,
|
||||
TRANSLATE_Z,
|
||||
YAW,
|
||||
STEP_TRANSLATE_X,
|
||||
STEP_TRANSLATE_Y,
|
||||
STEP_TRANSLATE_Z,
|
||||
STEP_YAW,
|
||||
PITCH,
|
||||
ZOOM,
|
||||
MAX_DRIVE_KEYS
|
||||
};
|
||||
|
||||
enum eyeContactTarget {
|
||||
LEFT_EYE,
|
||||
RIGHT_EYE,
|
||||
|
@ -34,7 +48,6 @@ enum AudioListenerMode {
|
|||
};
|
||||
Q_DECLARE_METATYPE(AudioListenerMode);
|
||||
|
||||
|
||||
class MyAvatar : public Avatar {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool shouldRenderLocally READ getShouldRenderLocally WRITE setShouldRenderLocally)
|
||||
|
@ -50,6 +63,12 @@ class MyAvatar : public Avatar {
|
|||
Q_PROPERTY(AudioListenerMode CUSTOM READ getAudioListenerModeCustom)
|
||||
//TODO: make gravity feature work Q_PROPERTY(glm::vec3 gravity READ getGravity WRITE setGravity)
|
||||
|
||||
|
||||
Q_PROPERTY(glm::vec3 leftHandPosition READ getLeftHandPosition)
|
||||
Q_PROPERTY(glm::vec3 rightHandPosition READ getRightHandPosition)
|
||||
Q_PROPERTY(glm::vec3 leftHandTipPosition READ getLeftHandTipPosition)
|
||||
Q_PROPERTY(glm::vec3 rightHandTipPosition READ getRightHandTipPosition)
|
||||
|
||||
public:
|
||||
MyAvatar(RigPointer rig);
|
||||
~MyAvatar();
|
||||
|
@ -136,6 +155,11 @@ public:
|
|||
|
||||
Q_INVOKABLE glm::vec3 getTargetAvatarPosition() const { return _targetAvatarPosition; }
|
||||
|
||||
Q_INVOKABLE glm::vec3 getLeftHandPosition() const;
|
||||
Q_INVOKABLE glm::vec3 getRightHandPosition() const;
|
||||
Q_INVOKABLE glm::vec3 getLeftHandTipPosition() const;
|
||||
Q_INVOKABLE glm::vec3 getRightHandTipPosition() const;
|
||||
|
||||
AvatarWeakPointer getLookAtTargetAvatar() const { return _lookAtTargetAvatar; }
|
||||
void updateLookAtTargetAvatar();
|
||||
void clearLookAtTargetAvatar();
|
||||
|
@ -274,6 +298,9 @@ private:
|
|||
|
||||
void setVisibleInSceneIfReady(Model* model, render::ScenePointer scene, bool visiblity);
|
||||
|
||||
const PalmData* getActivePalm(int palmIndex) const;
|
||||
|
||||
|
||||
// derive avatar body position and orientation from the current HMD Sensor location.
|
||||
// results are in sensor space
|
||||
glm::mat4 deriveBodyFromHMDSensor() const;
|
||||
|
@ -363,6 +390,8 @@ private:
|
|||
AtRestDetector _hmdAtRestDetector;
|
||||
glm::vec3 _lastPosition;
|
||||
bool _lastIsMoving = false;
|
||||
quint64 _lastStepPulse = 0;
|
||||
bool _pulseUpdate { false };
|
||||
};
|
||||
|
||||
QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode);
|
||||
|
|
|
@ -33,18 +33,14 @@ ConnexionData::ConnexionData() : InputDevice("ConnexionClient") {}
|
|||
|
||||
|
||||
void ConnexionData::handleAxisEvent() {
|
||||
_axisStateMap[makeInput(ROTATION_AXIS_Y_POS).getChannel()] = (cc_rotation.y > 0.0f) ? cc_rotation.y / MAX_AXIS : 0.0f;
|
||||
_axisStateMap[makeInput(ROTATION_AXIS_Y_NEG).getChannel()] = (cc_rotation.y < 0.0f) ? -cc_rotation.y / MAX_AXIS : 0.0f;
|
||||
_axisStateMap[makeInput(POSITION_AXIS_X_POS).getChannel()] = (cc_position.x > 0.0f) ? cc_position.x / MAX_AXIS : 0.0f;
|
||||
_axisStateMap[makeInput(POSITION_AXIS_X_NEG).getChannel()] = (cc_position.x < 0.0f) ? -cc_position.x / MAX_AXIS : 0.0f;
|
||||
_axisStateMap[makeInput(POSITION_AXIS_Y_POS).getChannel()] = (cc_position.y > 0.0f) ? cc_position.y / MAX_AXIS : 0.0f;
|
||||
_axisStateMap[makeInput(POSITION_AXIS_Y_NEG).getChannel()] = (cc_position.y < 0.0f) ? -cc_position.y / MAX_AXIS : 0.0f;
|
||||
_axisStateMap[makeInput(POSITION_AXIS_Z_POS).getChannel()] = (cc_position.z > 0.0f) ? cc_position.z / MAX_AXIS : 0.0f;
|
||||
_axisStateMap[makeInput(POSITION_AXIS_Z_NEG).getChannel()] = (cc_position.z < 0.0f) ? -cc_position.z / MAX_AXIS : 0.0f;
|
||||
_axisStateMap[makeInput(ROTATION_AXIS_X_POS).getChannel()] = (cc_rotation.x > 0.0f) ? cc_rotation.x / MAX_AXIS : 0.0f;
|
||||
_axisStateMap[makeInput(ROTATION_AXIS_X_NEG).getChannel()] = (cc_rotation.x < 0.0f) ? -cc_rotation.x / MAX_AXIS : 0.0f;
|
||||
_axisStateMap[makeInput(ROTATION_AXIS_Z_POS).getChannel()] = (cc_rotation.z > 0.0f) ? cc_rotation.z / MAX_AXIS : 0.0f;
|
||||
_axisStateMap[makeInput(ROTATION_AXIS_Z_NEG).getChannel()] = (cc_rotation.z < 0.0f) ? -cc_rotation.z / MAX_AXIS : 0.0f;
|
||||
auto rotation = cc_rotation / MAX_AXIS;
|
||||
_axisStateMap[ROTATE_X] = rotation.x;
|
||||
_axisStateMap[ROTATE_Y] = rotation.y;
|
||||
_axisStateMap[ROTATE_Z] = rotation.z;
|
||||
auto position = cc_rotation / MAX_AXIS;
|
||||
_axisStateMap[TRANSLATE_X] = position.x;
|
||||
_axisStateMap[TRANSLATE_Y] = position.y;
|
||||
_axisStateMap[TRANSLATE_Z] = position.z;
|
||||
}
|
||||
|
||||
void ConnexionData::setButton(int lastButtonState) {
|
||||
|
@ -57,24 +53,18 @@ void ConnexionData::buildDeviceProxy(controller::DeviceProxy::Pointer proxy) {
|
|||
proxy->getButton = [this](const controller::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); };
|
||||
proxy->getAxis = [this](const controller::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); };
|
||||
proxy->getAvailabeInputs = [this]() -> QVector<controller::Input::NamedPair> {
|
||||
QVector<controller::Input::NamedPair> availableInputs;
|
||||
|
||||
availableInputs.append(controller::Input::NamedPair(makeInput(BUTTON_1), "Left button"));
|
||||
availableInputs.append(controller::Input::NamedPair(makeInput(BUTTON_2), "Right button"));
|
||||
availableInputs.append(controller::Input::NamedPair(makeInput(BUTTON_3), "Both buttons"));
|
||||
|
||||
availableInputs.append(controller::Input::NamedPair(makeInput(POSITION_AXIS_Y_NEG), "Move backward"));
|
||||
availableInputs.append(controller::Input::NamedPair(makeInput(POSITION_AXIS_Y_POS), "Move forward"));
|
||||
availableInputs.append(controller::Input::NamedPair(makeInput(POSITION_AXIS_X_POS), "Move right"));
|
||||
availableInputs.append(controller::Input::NamedPair(makeInput(POSITION_AXIS_X_NEG), "Move Left"));
|
||||
availableInputs.append(controller::Input::NamedPair(makeInput(POSITION_AXIS_Z_POS), "Move up"));
|
||||
availableInputs.append(controller::Input::NamedPair(makeInput(POSITION_AXIS_Z_NEG), "Move down"));
|
||||
availableInputs.append(controller::Input::NamedPair(makeInput(ROTATION_AXIS_Y_NEG), "Rotate backward"));
|
||||
availableInputs.append(controller::Input::NamedPair(makeInput(ROTATION_AXIS_Y_POS), "Rotate forward"));
|
||||
availableInputs.append(controller::Input::NamedPair(makeInput(ROTATION_AXIS_X_POS), "Rotate right"));
|
||||
availableInputs.append(controller::Input::NamedPair(makeInput(ROTATION_AXIS_X_NEG), "Rotate left"));
|
||||
availableInputs.append(controller::Input::NamedPair(makeInput(ROTATION_AXIS_Z_POS), "Rotate up"));
|
||||
availableInputs.append(controller::Input::NamedPair(makeInput(ROTATION_AXIS_Z_NEG), "Rotate down"));
|
||||
using namespace controller;
|
||||
static QVector<controller::Input::NamedPair> availableInputs {
|
||||
Input::NamedPair(makeInput(BUTTON_1), "LeftButton"),
|
||||
Input::NamedPair(makeInput(BUTTON_2), "RightButton"),
|
||||
Input::NamedPair(makeInput(BUTTON_3), "BothButtons"),
|
||||
Input::NamedPair(makeInput(TRANSLATE_X), "TranslateX"),
|
||||
Input::NamedPair(makeInput(TRANSLATE_Y), "TranslateY"),
|
||||
Input::NamedPair(makeInput(TRANSLATE_Z), "TranslateZ"),
|
||||
Input::NamedPair(makeInput(ROTATE_X), "RotateX"),
|
||||
Input::NamedPair(makeInput(ROTATE_Y), "RotateY"),
|
||||
Input::NamedPair(makeInput(ROTATE_Z), "RotateZ"),
|
||||
};
|
||||
return availableInputs;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -182,18 +182,12 @@ public:
|
|||
static ConnexionData& getInstance();
|
||||
ConnexionData();
|
||||
enum PositionChannel {
|
||||
POSITION_AXIS_X_POS = 1,
|
||||
POSITION_AXIS_X_NEG = 2,
|
||||
POSITION_AXIS_Y_POS = 3,
|
||||
POSITION_AXIS_Y_NEG = 4,
|
||||
POSITION_AXIS_Z_POS = 5,
|
||||
POSITION_AXIS_Z_NEG = 6,
|
||||
ROTATION_AXIS_X_POS = 7,
|
||||
ROTATION_AXIS_X_NEG = 8,
|
||||
ROTATION_AXIS_Y_POS = 9,
|
||||
ROTATION_AXIS_Y_NEG = 10,
|
||||
ROTATION_AXIS_Z_POS = 11,
|
||||
ROTATION_AXIS_Z_NEG = 12
|
||||
TRANSLATE_X,
|
||||
TRANSLATE_Y,
|
||||
TRANSLATE_Z,
|
||||
ROTATE_X,
|
||||
ROTATE_Y,
|
||||
ROTATE_Z,
|
||||
};
|
||||
|
||||
enum ButtonChannel {
|
||||
|
|
|
@ -12,6 +12,23 @@
|
|||
|
||||
namespace controller {
|
||||
|
||||
Input::NamedPair makePair(ChannelType type, Action action, const QString& name) {
|
||||
auto input = Input(UserInputMapper::ACTIONS_DEVICE, toInt(action), type);
|
||||
return Input::NamedPair(input, name);
|
||||
}
|
||||
|
||||
Input::NamedPair makeAxisPair(Action action, const QString& name) {
|
||||
return makePair(ChannelType::AXIS, action, name);
|
||||
}
|
||||
|
||||
Input::NamedPair makeButtonPair(Action action, const QString& name) {
|
||||
return makePair(ChannelType::BUTTON, action, name);
|
||||
}
|
||||
|
||||
Input::NamedPair makePosePair(Action action, const QString& name) {
|
||||
return makePair(ChannelType::POSE, action, name);
|
||||
}
|
||||
|
||||
// Device functions
|
||||
void ActionsDevice::buildDeviceProxy(DeviceProxy::Pointer proxy) {
|
||||
proxy->_name = _name;
|
||||
|
@ -19,33 +36,69 @@ namespace controller {
|
|||
proxy->getAxis = [this](const Input& input, int timestamp) -> float { return 0; };
|
||||
proxy->getAvailabeInputs = [this]() -> QVector<Input::NamedPair> {
|
||||
QVector<Input::NamedPair> availableInputs{
|
||||
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::LONGITUDINAL_BACKWARD), ChannelType::AXIS), "LONGITUDINAL_BACKWARD"),
|
||||
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::LONGITUDINAL_FORWARD), ChannelType::AXIS), "LONGITUDINAL_FORWARD"),
|
||||
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::LATERAL_LEFT), ChannelType::AXIS), "LATERAL_LEFT"),
|
||||
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::LATERAL_RIGHT), ChannelType::AXIS), "LATERAL_RIGHT"),
|
||||
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::VERTICAL_DOWN), ChannelType::AXIS), "VERTICAL_DOWN"),
|
||||
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::VERTICAL_UP), ChannelType::AXIS), "VERTICAL_UP"),
|
||||
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::YAW_LEFT), ChannelType::AXIS), "YAW_LEFT"),
|
||||
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::YAW_RIGHT), ChannelType::AXIS), "YAW_RIGHT"),
|
||||
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::PITCH_DOWN), ChannelType::AXIS), "PITCH_DOWN"),
|
||||
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::PITCH_UP), ChannelType::AXIS), "PITCH_UP"),
|
||||
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::BOOM_IN), ChannelType::AXIS), "BOOM_IN"),
|
||||
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::BOOM_OUT), ChannelType::AXIS), "BOOM_OUT"),
|
||||
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::LEFT_HAND), ChannelType::POSE), "LEFT_HAND"),
|
||||
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::RIGHT_HAND), ChannelType::POSE), "RIGHT_HAND"),
|
||||
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::LEFT_HAND_CLICK), ChannelType::BUTTON), "LEFT_HAND_CLICK"),
|
||||
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::RIGHT_HAND_CLICK), ChannelType::BUTTON), "RIGHT_HAND_CLICK"),
|
||||
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::SHIFT), ChannelType::BUTTON), "SHIFT"),
|
||||
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::ACTION1), ChannelType::BUTTON), "ACTION1"),
|
||||
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::ACTION2), ChannelType::BUTTON), "ACTION2"),
|
||||
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::CONTEXT_MENU), ChannelType::BUTTON), "CONTEXT_MENU"),
|
||||
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::TOGGLE_MUTE), ChannelType::AXIS), "TOGGLE_MUTE"),
|
||||
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::TRANSLATE_X), ChannelType::AXIS), "TranslateX"),
|
||||
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::TRANSLATE_Y), ChannelType::AXIS), "TranslateY"),
|
||||
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::TRANSLATE_Z), ChannelType::AXIS), "TranslateZ"),
|
||||
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::ROLL), ChannelType::AXIS), "Roll"),
|
||||
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::PITCH), ChannelType::AXIS), "Pitch"),
|
||||
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::YAW), ChannelType::AXIS), "Yaw")
|
||||
makeAxisPair(Action::TRANSLATE_X, "TranslateX"),
|
||||
makeAxisPair(Action::TRANSLATE_Y, "TranslateY"),
|
||||
makeAxisPair(Action::TRANSLATE_Z, "TranslateZ"),
|
||||
makeAxisPair(Action::ROLL, "Roll"),
|
||||
makeAxisPair(Action::PITCH, "Pitch"),
|
||||
makeAxisPair(Action::YAW, "Yaw"),
|
||||
makeAxisPair(Action::STEP_YAW, "StepYaw"),
|
||||
makeAxisPair(Action::STEP_PITCH, "StepPitch"),
|
||||
makeAxisPair(Action::STEP_ROLL, "StepRoll"),
|
||||
makeAxisPair(Action::STEP_TRANSLATE_X, "StepTranslateX"),
|
||||
makeAxisPair(Action::STEP_TRANSLATE_X, "StepTranslateY"),
|
||||
makeAxisPair(Action::STEP_TRANSLATE_X, "StepTranslateZ"),
|
||||
makeAxisPair(Action::LONGITUDINAL_BACKWARD, "Backward"),
|
||||
makeAxisPair(Action::LONGITUDINAL_FORWARD, "Forward"),
|
||||
makeAxisPair(Action::LATERAL_LEFT, "StrafeLeft"),
|
||||
makeAxisPair(Action::LATERAL_RIGHT, "StrafeRight"),
|
||||
makeAxisPair(Action::VERTICAL_DOWN, "Down"),
|
||||
makeAxisPair(Action::VERTICAL_UP, "Up"),
|
||||
makeAxisPair(Action::YAW_LEFT, "YawLeft"),
|
||||
makeAxisPair(Action::YAW_RIGHT, "YawRight"),
|
||||
makeAxisPair(Action::PITCH_DOWN, "PitchDown"),
|
||||
makeAxisPair(Action::PITCH_UP, "PitchUp"),
|
||||
makeAxisPair(Action::BOOM_IN, "BoomIn"),
|
||||
makeAxisPair(Action::BOOM_OUT, "BoomOut"),
|
||||
|
||||
makePosePair(Action::LEFT_HAND, "LeftHand"),
|
||||
makePosePair(Action::RIGHT_HAND, "RightHand"),
|
||||
|
||||
makeButtonPair(Action::LEFT_HAND_CLICK, "LeftHandClick"),
|
||||
makeButtonPair(Action::RIGHT_HAND_CLICK, "RightHandClick"),
|
||||
|
||||
makeButtonPair(Action::SHIFT, "Shift"),
|
||||
makeButtonPair(Action::ACTION1, "PrimaryAction"),
|
||||
makeButtonPair(Action::ACTION2, "SecondaryAction"),
|
||||
makeButtonPair(Action::CONTEXT_MENU, "ContextMenu"),
|
||||
makeButtonPair(Action::TOGGLE_MUTE, "ToggleMute"),
|
||||
|
||||
// Deprecated aliases
|
||||
// FIXME remove after we port all scripts
|
||||
makeAxisPair(Action::LONGITUDINAL_BACKWARD, "LONGITUDINAL_BACKWARD"),
|
||||
makeAxisPair(Action::LONGITUDINAL_FORWARD, "LONGITUDINAL_FORWARD"),
|
||||
makeAxisPair(Action::LATERAL_LEFT, "LATERAL_LEFT"),
|
||||
makeAxisPair(Action::LATERAL_RIGHT, "LATERAL_RIGHT"),
|
||||
makeAxisPair(Action::VERTICAL_DOWN, "VERTICAL_DOWN"),
|
||||
makeAxisPair(Action::VERTICAL_UP, "VERTICAL_UP"),
|
||||
makeAxisPair(Action::YAW_LEFT, "YAW_LEFT"),
|
||||
makeAxisPair(Action::YAW_RIGHT, "YAW_RIGHT"),
|
||||
makeAxisPair(Action::PITCH_DOWN, "PITCH_DOWN"),
|
||||
makeAxisPair(Action::PITCH_UP, "PITCH_UP"),
|
||||
makeAxisPair(Action::BOOM_IN, "BOOM_IN"),
|
||||
makeAxisPair(Action::BOOM_OUT, "BOOM_OUT"),
|
||||
|
||||
makePosePair(Action::LEFT_HAND, "LEFT_HAND"),
|
||||
makePosePair(Action::RIGHT_HAND, "RIGHT_HAND"),
|
||||
|
||||
makeButtonPair(Action::LEFT_HAND_CLICK, "LEFT_HAND_CLICK"),
|
||||
makeButtonPair(Action::RIGHT_HAND_CLICK, "RIGHT_HAND_CLICK"),
|
||||
|
||||
makeButtonPair(Action::SHIFT, "SHIFT"),
|
||||
makeButtonPair(Action::ACTION1, "ACTION1"),
|
||||
makeButtonPair(Action::ACTION2, "ACTION2"),
|
||||
makeButtonPair(Action::CONTEXT_MENU, "CONTEXT_MENU"),
|
||||
makeButtonPair(Action::TOGGLE_MUTE, "TOGGLE_MUTE"),
|
||||
};
|
||||
return availableInputs;
|
||||
};
|
||||
|
|
|
@ -27,6 +27,16 @@ enum class Action {
|
|||
ROTATE_Y, YAW = ROTATE_Y,
|
||||
ROTATE_Z, ROLL = ROTATE_Z,
|
||||
|
||||
STEP_YAW,
|
||||
// FIXME does this have a use case?
|
||||
STEP_PITCH,
|
||||
// FIXME does this have a use case?
|
||||
STEP_ROLL,
|
||||
|
||||
STEP_TRANSLATE_X,
|
||||
STEP_TRANSLATE_Y,
|
||||
STEP_TRANSLATE_Z,
|
||||
|
||||
TRANSLATE_CAMERA_Z,
|
||||
NUM_COMBINED_AXES,
|
||||
|
||||
|
|
|
@ -18,14 +18,4 @@ namespace controller {
|
|||
return Conditional::Pointer();
|
||||
}
|
||||
|
||||
bool EndpointConditional::satisfied() {
|
||||
if (!_endpoint) {
|
||||
return false;
|
||||
}
|
||||
auto value = _endpoint->value();
|
||||
if (value == 0.0f) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,8 +17,6 @@
|
|||
|
||||
#include <shared/Factory.h>
|
||||
|
||||
#include "Endpoint.h"
|
||||
|
||||
class QJsonValue;
|
||||
|
||||
namespace controller {
|
||||
|
@ -41,14 +39,6 @@ namespace controller {
|
|||
static Factory _factory;
|
||||
};
|
||||
|
||||
class EndpointConditional : public Conditional {
|
||||
public:
|
||||
EndpointConditional(Endpoint::Pointer endpoint) : _endpoint(endpoint) { }
|
||||
virtual bool satisfied() override;
|
||||
private:
|
||||
Endpoint::Pointer _endpoint;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#define REGISTER_CONDITIONAL_CLASS(classEntry) \
|
||||
|
|
|
@ -40,9 +40,12 @@ namespace controller {
|
|||
virtual void apply(float newValue, float oldValue, const Pointer& source) = 0;
|
||||
virtual Pose pose() { return Pose(); }
|
||||
virtual void apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) {}
|
||||
|
||||
virtual const bool isPose() { return _input.isPose(); }
|
||||
|
||||
virtual bool writeable() const { return true; }
|
||||
virtual bool readable() const { return true; }
|
||||
virtual void reset() { }
|
||||
|
||||
const Input& getInput() { return _input; }
|
||||
|
||||
protected:
|
||||
|
@ -61,6 +64,26 @@ namespace controller {
|
|||
ReadLambda _readLambda;
|
||||
WriteLambda _writeLambda;
|
||||
};
|
||||
|
||||
|
||||
class VirtualEndpoint : public Endpoint {
|
||||
public:
|
||||
VirtualEndpoint(const Input& id = Input::INVALID_INPUT)
|
||||
: Endpoint(id) {
|
||||
}
|
||||
|
||||
virtual float value() override { return _currentValue; }
|
||||
virtual void apply(float newValue, float oldValue, const Pointer& source) override { _currentValue = newValue; }
|
||||
|
||||
virtual Pose pose() override { return _currentPose; }
|
||||
virtual void apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) override {
|
||||
_currentPose = newValue;
|
||||
}
|
||||
protected:
|
||||
float _currentValue { 0.0f };
|
||||
Pose _currentPose {};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -55,6 +55,7 @@ struct Input {
|
|||
Input(const Input& src) : id(src.id) {}
|
||||
Input& operator = (const Input& src) { id = src.id; return (*this); }
|
||||
bool operator ==(const Input& right) const { return INVALID_INPUT.id != id && INVALID_INPUT.id != right.id && id == right.id; }
|
||||
bool operator !=(const Input& right) const { return !(*this == right); }
|
||||
bool operator < (const Input& src) const { return id < src.id; }
|
||||
|
||||
static const Input INVALID_INPUT;
|
||||
|
|
|
@ -33,6 +33,7 @@ namespace controller {
|
|||
Pose(const Pose&) = default;
|
||||
Pose& operator = (const Pose&) = default;
|
||||
bool operator ==(const Pose& right) const;
|
||||
bool operator !=(const Pose& right) const { return !(*this == right); }
|
||||
bool isValid() const { return valid; }
|
||||
vec3 getTranslation() const { return translation; }
|
||||
quat getRotation() const { return rotation; }
|
||||
|
|
|
@ -95,10 +95,10 @@ void StandardController::buildDeviceProxy(DeviceProxy::Pointer proxy) {
|
|||
availableInputs.append(makePair(DR, "Right"));
|
||||
|
||||
|
||||
availableInputs.append(makePair(LeftPrimaryThumb, "LeftPrimaryThumb"));
|
||||
availableInputs.append(makePair(LeftSecondaryThumb, "LeftSecondaryThumb"));
|
||||
availableInputs.append(makePair(RightPrimaryThumb, "RightPrimaryThumb"));
|
||||
availableInputs.append(makePair(RightSecondaryThumb, "RightSecondaryThumb"));
|
||||
availableInputs.append(makePair(LEFT_PRIMARY_THUMB, "LeftPrimaryThumb"));
|
||||
availableInputs.append(makePair(LEFT_SECONDARY_THUMB, "LeftSecondaryThumb"));
|
||||
availableInputs.append(makePair(RIGHT_PRIMARY_THUMB, "RightPrimaryThumb"));
|
||||
availableInputs.append(makePair(RIGHT_SECONDARY_THUMB, "RightSecondaryThumb"));
|
||||
|
||||
return availableInputs;
|
||||
};
|
||||
|
|
|
@ -37,10 +37,10 @@ namespace controller {
|
|||
DR,
|
||||
|
||||
// These don't map to SDL types
|
||||
LeftPrimaryThumb,
|
||||
LeftSecondaryThumb,
|
||||
RightPrimaryThumb,
|
||||
RightSecondaryThumb,
|
||||
LEFT_PRIMARY_THUMB,
|
||||
LEFT_SECONDARY_THUMB,
|
||||
RIGHT_PRIMARY_THUMB,
|
||||
RIGHT_SECONDARY_THUMB,
|
||||
|
||||
NUM_STANDARD_BUTTONS
|
||||
};
|
||||
|
|
|
@ -29,7 +29,6 @@ namespace controller {
|
|||
|
||||
// Default contruct allocate the poutput size with the current hardcoded action channels
|
||||
controller::UserInputMapper::UserInputMapper() {
|
||||
_activeMappings.push_back(_defaultMapping);
|
||||
_standardController = std::make_shared<StandardController>();
|
||||
registerDevice(new ActionsDevice());
|
||||
registerDevice(_standardController.get());
|
||||
|
@ -55,22 +54,45 @@ private:
|
|||
float _lastValue = 0.0f;
|
||||
};
|
||||
|
||||
class VirtualEndpoint : public Endpoint {
|
||||
class StandardEndpoint : public VirtualEndpoint {
|
||||
public:
|
||||
VirtualEndpoint(const Input& id = Input::INVALID_INPUT)
|
||||
: Endpoint(id) {
|
||||
StandardEndpoint(const Input& input) : VirtualEndpoint(input) {}
|
||||
virtual bool writeable() const override { return !_written; }
|
||||
virtual bool readable() const override { return !_read; }
|
||||
virtual void reset() override {
|
||||
apply(0.0f, 0.0f, Endpoint::Pointer());
|
||||
apply(Pose(), Pose(), Endpoint::Pointer());
|
||||
_written = _read = false;
|
||||
}
|
||||
|
||||
virtual float value() override { return _currentValue; }
|
||||
virtual void apply(float newValue, float oldValue, const Pointer& source) override { _currentValue = newValue; }
|
||||
virtual float value() override {
|
||||
_read = true;
|
||||
return VirtualEndpoint::value();
|
||||
}
|
||||
|
||||
virtual void apply(float newValue, float oldValue, const Pointer& source) override {
|
||||
// For standard endpoints, the first NON-ZERO write counts.
|
||||
if (newValue != 0.0) {
|
||||
_written = true;
|
||||
}
|
||||
VirtualEndpoint::apply(newValue, oldValue, source);
|
||||
}
|
||||
|
||||
virtual Pose pose() override {
|
||||
_read = true;
|
||||
return VirtualEndpoint::pose();
|
||||
}
|
||||
|
||||
virtual Pose pose() override { return _currentPose; }
|
||||
virtual void apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) override {
|
||||
_currentPose = newValue;
|
||||
if (newValue != Pose()) {
|
||||
_written = true;
|
||||
}
|
||||
VirtualEndpoint::apply(newValue, oldValue, source);
|
||||
}
|
||||
|
||||
private:
|
||||
float _currentValue{ 0.0f };
|
||||
Pose _currentPose{};
|
||||
bool _written { false };
|
||||
bool _read { false };
|
||||
};
|
||||
|
||||
|
||||
|
@ -136,12 +158,67 @@ public:
|
|||
virtual void apply(float newValue, float oldValue, const Pointer& source) {
|
||||
// Composites are read only
|
||||
}
|
||||
|
||||
private:
|
||||
Endpoint::Pointer _first;
|
||||
Endpoint::Pointer _second;
|
||||
};
|
||||
|
||||
class ArrayEndpoint : public Endpoint {
|
||||
friend class UserInputMapper;
|
||||
public:
|
||||
using Pointer = std::shared_ptr<ArrayEndpoint>;
|
||||
ArrayEndpoint() : Endpoint(Input::INVALID_INPUT) { }
|
||||
|
||||
virtual float value() override {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
virtual void apply(float newValue, float oldValue, const Endpoint::Pointer& source) override {
|
||||
for (auto& child : _children) {
|
||||
if (child->writeable()) {
|
||||
child->apply(newValue, oldValue, source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool readable() const override { return false; }
|
||||
|
||||
private:
|
||||
Endpoint::List _children;
|
||||
};
|
||||
|
||||
class AnyEndpoint : public Endpoint {
|
||||
friend class UserInputMapper;
|
||||
public:
|
||||
using Pointer = std::shared_ptr<AnyEndpoint>;
|
||||
AnyEndpoint() : Endpoint(Input::INVALID_INPUT) {}
|
||||
|
||||
virtual float value() override {
|
||||
float result = 0;
|
||||
for (auto& child : _children) {
|
||||
float childResult = child->value();
|
||||
if (childResult != 0.0f) {
|
||||
result = childResult;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
virtual void apply(float newValue, float oldValue, const Endpoint::Pointer& source) override {
|
||||
qFatal("AnyEndpoint is read only");
|
||||
}
|
||||
|
||||
virtual bool writeable() const override { return false; }
|
||||
|
||||
virtual bool readable() const override {
|
||||
for (auto& child : _children) {
|
||||
if (!child->readable()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
Endpoint::List _children;
|
||||
};
|
||||
|
||||
class InputEndpoint : public Endpoint {
|
||||
public:
|
||||
|
@ -150,40 +227,44 @@ public:
|
|||
}
|
||||
|
||||
virtual float value() override {
|
||||
_currentValue = 0.0f;
|
||||
_read = true;
|
||||
if (isPose()) {
|
||||
return _currentValue;
|
||||
return pose().valid ? 1.0f : 0.0f;
|
||||
}
|
||||
auto userInputMapper = DependencyManager::get<UserInputMapper>();
|
||||
auto deviceProxy = userInputMapper->getDeviceProxy(_input);
|
||||
if (!deviceProxy) {
|
||||
return _currentValue;
|
||||
return 0.0f;
|
||||
}
|
||||
_currentValue = deviceProxy->getValue(_input, 0);
|
||||
return _currentValue;
|
||||
return deviceProxy->getValue(_input, 0);
|
||||
}
|
||||
|
||||
// FIXME need support for writing back to vibration / force feedback effects
|
||||
virtual void apply(float newValue, float oldValue, const Pointer& source) override {}
|
||||
|
||||
virtual Pose pose() override {
|
||||
_currentPose = Pose();
|
||||
_read = true;
|
||||
if (!isPose()) {
|
||||
return _currentPose;
|
||||
return Pose();
|
||||
}
|
||||
auto userInputMapper = DependencyManager::get<UserInputMapper>();
|
||||
auto deviceProxy = userInputMapper->getDeviceProxy(_input);
|
||||
if (!deviceProxy) {
|
||||
return _currentPose;
|
||||
return Pose();
|
||||
}
|
||||
_currentPose = deviceProxy->getPose(_input, 0);
|
||||
return _currentPose;
|
||||
return deviceProxy->getPose(_input, 0);
|
||||
}
|
||||
|
||||
virtual void apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) override {
|
||||
}
|
||||
virtual void apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) override { }
|
||||
|
||||
virtual bool writeable() const { return !_written; }
|
||||
virtual bool readable() const { return !_read; }
|
||||
virtual void reset() { _written = _read = false; }
|
||||
|
||||
private:
|
||||
float _currentValue{ 0.0f };
|
||||
Pose _currentPose{};
|
||||
|
||||
bool _written { false };
|
||||
bool _read { false };
|
||||
};
|
||||
|
||||
class ActionEndpoint : public Endpoint {
|
||||
|
@ -194,9 +275,8 @@ public:
|
|||
|
||||
virtual float value() override { return _currentValue; }
|
||||
virtual void apply(float newValue, float oldValue, const Pointer& source) override {
|
||||
|
||||
_currentValue += newValue;
|
||||
if (!(_input == Input::INVALID_INPUT)) {
|
||||
if (_input != Input::INVALID_INPUT) {
|
||||
auto userInputMapper = DependencyManager::get<UserInputMapper>();
|
||||
userInputMapper->deltaActionState(Action(_input.getChannel()), newValue);
|
||||
}
|
||||
|
@ -208,12 +288,17 @@ public:
|
|||
if (!_currentPose.isValid()) {
|
||||
return;
|
||||
}
|
||||
if (!(_input == Input::INVALID_INPUT)) {
|
||||
if (_input != Input::INVALID_INPUT) {
|
||||
auto userInputMapper = DependencyManager::get<UserInputMapper>();
|
||||
userInputMapper->setActionState(Action(_input.getChannel()), _currentPose);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void reset() override {
|
||||
_currentValue = 0.0f;
|
||||
_currentPose = Pose();
|
||||
}
|
||||
|
||||
private:
|
||||
float _currentValue{ 0.0f };
|
||||
Pose _currentPose{};
|
||||
|
@ -231,6 +316,7 @@ int UserInputMapper::recordDeviceOfType(const QString& deviceName) {
|
|||
}
|
||||
|
||||
void UserInputMapper::registerDevice(InputDevice* device) {
|
||||
Locker locker(_lock);
|
||||
if (device->_deviceID == Input::INVALID_DEVICE) {
|
||||
device->_deviceID = getFreeDeviceID();
|
||||
}
|
||||
|
@ -254,7 +340,7 @@ void UserInputMapper::registerDevice(InputDevice* device) {
|
|||
}
|
||||
Endpoint::Pointer endpoint;
|
||||
if (input.device == STANDARD_DEVICE) {
|
||||
endpoint = std::make_shared<VirtualEndpoint>(input);
|
||||
endpoint = std::make_shared<StandardEndpoint>(input);
|
||||
} else if (input.device == ACTIONS_DEVICE) {
|
||||
endpoint = std::make_shared<ActionEndpoint>(input);
|
||||
} else {
|
||||
|
@ -268,13 +354,7 @@ void UserInputMapper::registerDevice(InputDevice* device) {
|
|||
auto mapping = loadMapping(device->getDefaultMappingConfig());
|
||||
if (mapping) {
|
||||
_mappingsByDevice[deviceID] = mapping;
|
||||
auto& defaultRoutes = _defaultMapping->routes;
|
||||
|
||||
// New routes for a device get injected IN FRONT of existing routes. Routes
|
||||
// are processed in order so this ensures that the standard -> action processing
|
||||
// takes place after all of the hardware -> standard or hardware -> action processing
|
||||
// because standard -> action is the first set of routes added.
|
||||
defaultRoutes.insert(defaultRoutes.begin(), mapping->routes.begin(), mapping->routes.end());
|
||||
enableMapping(mapping);
|
||||
}
|
||||
|
||||
emit hardwareChanged();
|
||||
|
@ -282,6 +362,7 @@ void UserInputMapper::registerDevice(InputDevice* device) {
|
|||
|
||||
// FIXME remove the associated device mappings
|
||||
void UserInputMapper::removeDevice(int deviceID) {
|
||||
Locker locker(_lock);
|
||||
auto proxyEntry = _registeredDevices.find(deviceID);
|
||||
if (_registeredDevices.end() == proxyEntry) {
|
||||
qCWarning(controllers) << "Attempted to remove unknown device " << deviceID;
|
||||
|
@ -290,15 +371,7 @@ void UserInputMapper::removeDevice(int deviceID) {
|
|||
auto proxy = proxyEntry->second;
|
||||
auto mappingsEntry = _mappingsByDevice.find(deviceID);
|
||||
if (_mappingsByDevice.end() != mappingsEntry) {
|
||||
const auto& mapping = mappingsEntry->second;
|
||||
const auto& deviceRoutes = mapping->routes;
|
||||
std::set<Route::Pointer> routeSet(deviceRoutes.begin(), deviceRoutes.end());
|
||||
|
||||
auto& defaultRoutes = _defaultMapping->routes;
|
||||
std::remove_if(defaultRoutes.begin(), defaultRoutes.end(), [&](Route::Pointer route)->bool {
|
||||
return routeSet.count(route) != 0;
|
||||
});
|
||||
|
||||
disableMapping(mappingsEntry->second);
|
||||
_mappingsByDevice.erase(mappingsEntry);
|
||||
}
|
||||
|
||||
|
@ -309,6 +382,7 @@ void UserInputMapper::removeDevice(int deviceID) {
|
|||
|
||||
|
||||
DeviceProxy::Pointer UserInputMapper::getDeviceProxy(const Input& input) {
|
||||
Locker locker(_lock);
|
||||
auto device = _registeredDevices.find(input.getDevice());
|
||||
if (device != _registeredDevices.end()) {
|
||||
return (device->second);
|
||||
|
@ -318,6 +392,7 @@ DeviceProxy::Pointer UserInputMapper::getDeviceProxy(const Input& input) {
|
|||
}
|
||||
|
||||
QString UserInputMapper::getDeviceName(uint16 deviceID) {
|
||||
Locker locker(_lock);
|
||||
if (_registeredDevices.find(deviceID) != _registeredDevices.end()) {
|
||||
return _registeredDevices[deviceID]->_name;
|
||||
}
|
||||
|
@ -325,6 +400,7 @@ QString UserInputMapper::getDeviceName(uint16 deviceID) {
|
|||
}
|
||||
|
||||
int UserInputMapper::findDevice(QString name) const {
|
||||
Locker locker(_lock);
|
||||
for (auto device : _registeredDevices) {
|
||||
if (device.second->_name == name) {
|
||||
return device.first;
|
||||
|
@ -334,6 +410,7 @@ int UserInputMapper::findDevice(QString name) const {
|
|||
}
|
||||
|
||||
QVector<QString> UserInputMapper::getDeviceNames() {
|
||||
Locker locker(_lock);
|
||||
QVector<QString> result;
|
||||
for (auto device : _registeredDevices) {
|
||||
QString deviceName = device.second->_name.split(" (")[0];
|
||||
|
@ -347,6 +424,7 @@ int UserInputMapper::findAction(const QString& actionName) const {
|
|||
}
|
||||
|
||||
Input UserInputMapper::findDeviceInput(const QString& inputName) const {
|
||||
Locker locker(_lock);
|
||||
// Split the full input name as such: deviceName.inputName
|
||||
auto names = inputName.split('.');
|
||||
|
||||
|
@ -386,6 +464,7 @@ void fixBisectedAxis(float& full, float& negative, float& positive) {
|
|||
}
|
||||
|
||||
void UserInputMapper::update(float deltaTime) {
|
||||
Locker locker(_lock);
|
||||
// Reset the axis state for next loop
|
||||
for (auto& channel : _actionStates) {
|
||||
channel = 0.0f;
|
||||
|
@ -396,20 +475,7 @@ void UserInputMapper::update(float deltaTime) {
|
|||
}
|
||||
|
||||
// Run the mappings code
|
||||
update();
|
||||
|
||||
// Scale all the channel step with the scale
|
||||
for (auto i = 0; i < toInt(Action::NUM_ACTIONS); i++) {
|
||||
if (_externalActionStates[i] != 0) {
|
||||
_actionStates[i] += _externalActionStates[i];
|
||||
_externalActionStates[i] = 0.0f;
|
||||
}
|
||||
|
||||
if (_externalPoseStates[i].isValid()) {
|
||||
_poseStates[i] = _externalPoseStates[i];
|
||||
_externalPoseStates[i] = Pose();
|
||||
}
|
||||
}
|
||||
runMappings();
|
||||
|
||||
// merge the bisected and non-bisected axes for now
|
||||
fixBisectedAxis(_actionStates[toInt(Action::TRANSLATE_X)], _actionStates[toInt(Action::LATERAL_LEFT)], _actionStates[toInt(Action::LATERAL_RIGHT)]);
|
||||
|
@ -419,7 +485,6 @@ void UserInputMapper::update(float deltaTime) {
|
|||
fixBisectedAxis(_actionStates[toInt(Action::ROTATE_Y)], _actionStates[toInt(Action::YAW_LEFT)], _actionStates[toInt(Action::YAW_RIGHT)]);
|
||||
fixBisectedAxis(_actionStates[toInt(Action::ROTATE_X)], _actionStates[toInt(Action::PITCH_UP)], _actionStates[toInt(Action::PITCH_DOWN)]);
|
||||
|
||||
|
||||
static const float EPSILON = 0.01f;
|
||||
for (auto i = 0; i < toInt(Action::NUM_ACTIONS); i++) {
|
||||
_actionStates[i] *= _actionScales[i];
|
||||
|
@ -433,11 +498,13 @@ void UserInputMapper::update(float deltaTime) {
|
|||
}
|
||||
|
||||
Input::NamedVector UserInputMapper::getAvailableInputs(uint16 deviceID) const {
|
||||
Locker locker(_lock);
|
||||
auto iterator = _registeredDevices.find(deviceID);
|
||||
return iterator->second->getAvailabeInputs();
|
||||
}
|
||||
|
||||
QVector<Action> UserInputMapper::getAllActions() const {
|
||||
Locker locker(_lock);
|
||||
QVector<Action> actions;
|
||||
for (auto i = 0; i < toInt(Action::NUM_ACTIONS); i++) {
|
||||
actions.append(Action(i));
|
||||
|
@ -446,6 +513,7 @@ QVector<Action> UserInputMapper::getAllActions() const {
|
|||
}
|
||||
|
||||
QString UserInputMapper::getActionName(Action action) const {
|
||||
Locker locker(_lock);
|
||||
for (auto actionPair : getActionInputs()) {
|
||||
if (actionPair.first.channel == toInt(action)) {
|
||||
return actionPair.second;
|
||||
|
@ -456,6 +524,7 @@ QString UserInputMapper::getActionName(Action action) const {
|
|||
|
||||
|
||||
QVector<QString> UserInputMapper::getActionNames() const {
|
||||
Locker locker(_lock);
|
||||
QVector<QString> result;
|
||||
for (auto actionPair : getActionInputs()) {
|
||||
result << actionPair.second;
|
||||
|
@ -564,76 +633,95 @@ Input UserInputMapper::makeStandardInput(controller::StandardPoseChannel pose) {
|
|||
return Input(STANDARD_DEVICE, pose, ChannelType::POSE);
|
||||
}
|
||||
|
||||
void UserInputMapper::update() {
|
||||
void UserInputMapper::runMappings() {
|
||||
static auto deviceNames = getDeviceNames();
|
||||
_overrideValues.clear();
|
||||
_overrides.clear();
|
||||
|
||||
EndpointSet readEndpoints;
|
||||
EndpointSet writtenEndpoints;
|
||||
for (auto endpointEntry : this->_endpointsByInput) {
|
||||
endpointEntry.second->reset();
|
||||
}
|
||||
|
||||
// Now process the current values for each level of the stack
|
||||
for (auto& mapping : _activeMappings) {
|
||||
for (const auto& route : mapping->routes) {
|
||||
const auto& source = route->source;
|
||||
// Endpoints can only be read once (though a given mapping can route them to
|
||||
// multiple places). Consider... If the default is to wire the A button to JUMP
|
||||
// and someone else wires it to CONTEXT_MENU, I don't want both to occur when
|
||||
// I press the button. The exception is if I'm wiring a control back to itself
|
||||
// in order to adjust my interface, like inverting the Y axis on an analog stick
|
||||
if (readEndpoints.count(source)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& destination = route->destination;
|
||||
// THis could happen if the route destination failed to create
|
||||
// FIXME: Maybe do not create the route if the destination failed and avoid this case ?
|
||||
if (!destination) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (writtenEndpoints.count(destination)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (route->conditional) {
|
||||
if (!route->conditional->satisfied()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Standard controller destinations can only be can only be used once.
|
||||
if (getStandardDeviceID() == destination->getInput().getDevice()) {
|
||||
writtenEndpoints.insert(destination);
|
||||
}
|
||||
|
||||
// Only consume the input if the route isn't a loopback.
|
||||
// This allows mappings like `mapping.from(xbox.RY).invert().to(xbox.RY);`
|
||||
bool loopback = source == destination;
|
||||
if (!loopback) {
|
||||
readEndpoints.insert(source);
|
||||
}
|
||||
|
||||
// Fetch the value, may have been overriden by previous loopback routes
|
||||
if (source->isPose()) {
|
||||
Pose value = getPose(source);
|
||||
// no filters yet for pose
|
||||
destination->apply(value, Pose(), source);
|
||||
} else {
|
||||
// Fetch the value, may have been overriden by previous loopback routes
|
||||
float value = getValue(source);
|
||||
|
||||
// Apply each of the filters.
|
||||
for (const auto& filter : route->filters) {
|
||||
value = filter->apply(value);
|
||||
}
|
||||
|
||||
if (loopback) {
|
||||
_overrideValues[source] = value;
|
||||
} else {
|
||||
destination->apply(value, 0, source);
|
||||
}
|
||||
}
|
||||
for (const auto& route : _deviceRoutes) {
|
||||
if (!route) {
|
||||
continue;
|
||||
}
|
||||
applyRoute(route);
|
||||
}
|
||||
|
||||
for (const auto& route : _standardRoutes) {
|
||||
if (!route) {
|
||||
continue;
|
||||
}
|
||||
applyRoute(route);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void UserInputMapper::applyRoute(const Route::Pointer& route) {
|
||||
if (route->conditional) {
|
||||
if (!route->conditional->satisfied()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto source = route->source;
|
||||
if (_overrides.count(source)) {
|
||||
source = _overrides[source];
|
||||
}
|
||||
|
||||
// Endpoints can only be read once (though a given mapping can route them to
|
||||
// multiple places). Consider... If the default is to wire the A button to JUMP
|
||||
// and someone else wires it to CONTEXT_MENU, I don't want both to occur when
|
||||
// I press the button. The exception is if I'm wiring a control back to itself
|
||||
// in order to adjust my interface, like inverting the Y axis on an analog stick
|
||||
if (!source->readable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
auto input = source->getInput();
|
||||
float value = source->value();
|
||||
if (value != 0.0) {
|
||||
int i = 0;
|
||||
}
|
||||
|
||||
auto destination = route->destination;
|
||||
// THis could happen if the route destination failed to create
|
||||
// FIXME: Maybe do not create the route if the destination failed and avoid this case ?
|
||||
if (!destination) {
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME?, should come before or after the override logic?
|
||||
if (!destination->writeable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only consume the input if the route isn't a loopback.
|
||||
// This allows mappings like `mapping.from(xbox.RY).invert().to(xbox.RY);`
|
||||
bool loopback = (source->getInput() == destination->getInput()) && (source->getInput() != Input::INVALID_INPUT);
|
||||
// Each time we loop back we re-write the override
|
||||
if (loopback) {
|
||||
_overrides[source] = destination = std::make_shared<StandardEndpoint>(source->getInput());
|
||||
}
|
||||
|
||||
// Fetch the value, may have been overriden by previous loopback routes
|
||||
if (source->isPose()) {
|
||||
Pose value = getPose(source);
|
||||
// no filters yet for pose
|
||||
destination->apply(value, Pose(), source);
|
||||
} else {
|
||||
// Fetch the value, may have been overriden by previous loopback routes
|
||||
float value = getValue(source);
|
||||
|
||||
// Apply each of the filters.
|
||||
for (const auto& filter : route->filters) {
|
||||
value = filter->apply(value);
|
||||
}
|
||||
|
||||
destination->apply(value, 0, source);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -666,6 +754,7 @@ Endpoint::Pointer UserInputMapper::endpointFor(const QScriptValue& endpoint) {
|
|||
}
|
||||
|
||||
Endpoint::Pointer UserInputMapper::endpointFor(const Input& inputId) const {
|
||||
Locker locker(_lock);
|
||||
auto iterator = _endpointsByInput.find(inputId);
|
||||
if (_endpointsByInput.end() == iterator) {
|
||||
qWarning() << "Unknown input: " << QString::number(inputId.getID(), 16);
|
||||
|
@ -689,6 +778,7 @@ Endpoint::Pointer UserInputMapper::compositeEndpointFor(Endpoint::Pointer first,
|
|||
|
||||
|
||||
Mapping::Pointer UserInputMapper::newMapping(const QString& mappingName) {
|
||||
Locker locker(_lock);
|
||||
if (_mappingsByName.count(mappingName)) {
|
||||
qCWarning(controllers) << "Refusing to recreate mapping named " << mappingName;
|
||||
}
|
||||
|
@ -721,8 +811,8 @@ Mapping::Pointer UserInputMapper::newMapping(const QString& mappingName) {
|
|||
// return result;
|
||||
//}
|
||||
|
||||
|
||||
void UserInputMapper::enableMapping(const QString& mappingName, bool enable) {
|
||||
Locker locker(_lock);
|
||||
qCDebug(controllers) << "Attempting to enable mapping " << mappingName;
|
||||
auto iterator = _mappingsByName.find(mappingName);
|
||||
if (_mappingsByName.end() == iterator) {
|
||||
|
@ -732,21 +822,17 @@ void UserInputMapper::enableMapping(const QString& mappingName, bool enable) {
|
|||
|
||||
auto mapping = iterator->second;
|
||||
if (enable) {
|
||||
_activeMappings.push_front(mapping);
|
||||
enableMapping(mapping);
|
||||
} else {
|
||||
auto activeIterator = std::find(_activeMappings.begin(), _activeMappings.end(), mapping);
|
||||
if (_activeMappings.end() == activeIterator) {
|
||||
qCWarning(controllers) << "Attempted to disable inactive mapping " << mappingName;
|
||||
return;
|
||||
}
|
||||
_activeMappings.erase(activeIterator);
|
||||
disableMapping(mapping);
|
||||
}
|
||||
}
|
||||
|
||||
float UserInputMapper::getValue(const Endpoint::Pointer& endpoint) const {
|
||||
auto valuesIterator = _overrideValues.find(endpoint);
|
||||
if (_overrideValues.end() != valuesIterator) {
|
||||
return valuesIterator->second;
|
||||
Locker locker(_lock);
|
||||
auto valuesIterator = _overrides.find(endpoint);
|
||||
if (_overrides.end() != valuesIterator) {
|
||||
return valuesIterator->second->value();
|
||||
}
|
||||
|
||||
return endpoint->value();
|
||||
|
@ -776,6 +862,7 @@ Pose UserInputMapper::getPose(const Input& input) const {
|
|||
}
|
||||
|
||||
Mapping::Pointer UserInputMapper::loadMapping(const QString& jsonFile) {
|
||||
Locker locker(_lock);
|
||||
if (jsonFile.isEmpty()) {
|
||||
return Mapping::Pointer();
|
||||
}
|
||||
|
@ -790,27 +877,70 @@ Mapping::Pointer UserInputMapper::loadMapping(const QString& jsonFile) {
|
|||
return parseMapping(json);
|
||||
}
|
||||
|
||||
|
||||
const QString JSON_NAME = QStringLiteral("name");
|
||||
const QString JSON_CHANNELS = QStringLiteral("channels");
|
||||
const QString JSON_CHANNEL_FROM = QStringLiteral("from");
|
||||
const QString JSON_CHANNEL_WHEN = QStringLiteral("when");
|
||||
const QString JSON_CHANNEL_TO = QStringLiteral("to");
|
||||
const QString JSON_CHANNEL_FILTERS = QStringLiteral("filters");
|
||||
static const QString JSON_NAME = QStringLiteral("name");
|
||||
static const QString JSON_CHANNELS = QStringLiteral("channels");
|
||||
static const QString JSON_CHANNEL_FROM = QStringLiteral("from");
|
||||
static const QString JSON_CHANNEL_WHEN = QStringLiteral("when");
|
||||
static const QString JSON_CHANNEL_TO = QStringLiteral("to");
|
||||
static const QString JSON_CHANNEL_FILTERS = QStringLiteral("filters");
|
||||
|
||||
Endpoint::Pointer UserInputMapper::parseEndpoint(const QJsonValue& value) {
|
||||
Endpoint::Pointer result;
|
||||
if (value.isString()) {
|
||||
auto input = findDeviceInput(value.toString());
|
||||
return endpointFor(input);
|
||||
result = endpointFor(input);
|
||||
} else if (value.isObject()) {
|
||||
// Endpoint is defined as an object, we expect a js function then
|
||||
return Endpoint::Pointer();
|
||||
}
|
||||
return Endpoint::Pointer();
|
||||
|
||||
if (!result) {
|
||||
qWarning() << "Invalid endpoint definition " << value;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
class AndConditional : public Conditional {
|
||||
public:
|
||||
using Pointer = std::shared_ptr<AndConditional>;
|
||||
|
||||
AndConditional(Conditional::List children) : _children(children) { }
|
||||
|
||||
virtual bool satisfied() override {
|
||||
for (auto& conditional : _children) {
|
||||
if (!conditional->satisfied()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
Conditional::List _children;
|
||||
};
|
||||
|
||||
class EndpointConditional : public Conditional {
|
||||
public:
|
||||
EndpointConditional(Endpoint::Pointer endpoint) : _endpoint(endpoint) {}
|
||||
virtual bool satisfied() override { return _endpoint && _endpoint->value() != 0.0; }
|
||||
private:
|
||||
Endpoint::Pointer _endpoint;
|
||||
};
|
||||
|
||||
Conditional::Pointer UserInputMapper::parseConditional(const QJsonValue& value) {
|
||||
if (value.isString()) {
|
||||
if (value.isArray()) {
|
||||
// Support "when" : [ "GamePad.RB", "GamePad.LB" ]
|
||||
Conditional::List children;
|
||||
for (auto arrayItem : value.toArray()) {
|
||||
Conditional::Pointer childConditional = parseConditional(arrayItem);
|
||||
if (!childConditional) {
|
||||
return Conditional::Pointer();
|
||||
}
|
||||
children.push_back(childConditional);
|
||||
}
|
||||
return std::make_shared<AndConditional>(children);
|
||||
} else if (value.isString()) {
|
||||
// Support "when" : "GamePad.RB"
|
||||
auto input = findDeviceInput(value.toString());
|
||||
auto endpoint = endpointFor(input);
|
||||
if (!endpoint) {
|
||||
|
@ -818,11 +948,85 @@ Conditional::Pointer UserInputMapper::parseConditional(const QJsonValue& value)
|
|||
}
|
||||
|
||||
return std::make_shared<EndpointConditional>(endpoint);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return Conditional::parse(value);
|
||||
}
|
||||
|
||||
|
||||
Filter::Pointer UserInputMapper::parseFilter(const QJsonValue& value) {
|
||||
Filter::Pointer result;
|
||||
if (value.isString()) {
|
||||
result = Filter::getFactory().create(value.toString());
|
||||
} else if (value.isObject()) {
|
||||
result = Filter::parse(value.toObject());
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
qWarning() << "Invalid filter definition " << value;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Filter::List UserInputMapper::parseFilters(const QJsonValue& value) {
|
||||
if (value.isNull()) {
|
||||
return Filter::List();
|
||||
}
|
||||
|
||||
if (value.isArray()) {
|
||||
Filter::List result;
|
||||
auto filtersArray = value.toArray();
|
||||
for (auto filterValue : filtersArray) {
|
||||
Filter::Pointer filter = parseFilter(filterValue);
|
||||
if (!filter) {
|
||||
return Filter::List();
|
||||
}
|
||||
result.push_back(filter);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Filter::Pointer filter = parseFilter(value);
|
||||
if (!filter) {
|
||||
return Filter::List();
|
||||
}
|
||||
return Filter::List({ filter });
|
||||
}
|
||||
|
||||
Endpoint::Pointer UserInputMapper::parseDestination(const QJsonValue& value) {
|
||||
if (value.isArray()) {
|
||||
ArrayEndpoint::Pointer result = std::make_shared<ArrayEndpoint>();
|
||||
for (auto arrayItem : value.toArray()) {
|
||||
Endpoint::Pointer destination = parseEndpoint(arrayItem);
|
||||
if (!destination) {
|
||||
return Endpoint::Pointer();
|
||||
}
|
||||
result->_children.push_back(destination);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
return parseEndpoint(value);
|
||||
}
|
||||
|
||||
Endpoint::Pointer UserInputMapper::parseSource(const QJsonValue& value) {
|
||||
if (value.isArray()) {
|
||||
AnyEndpoint::Pointer result = std::make_shared<AnyEndpoint>();
|
||||
for (auto arrayItem : value.toArray()) {
|
||||
Endpoint::Pointer destination = parseEndpoint(arrayItem);
|
||||
if (!destination) {
|
||||
return Endpoint::Pointer();
|
||||
}
|
||||
result->_children.push_back(destination);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
return parseEndpoint(value);
|
||||
}
|
||||
|
||||
Route::Pointer UserInputMapper::parseRoute(const QJsonValue& value) {
|
||||
if (!value.isObject()) {
|
||||
return Route::Pointer();
|
||||
|
@ -830,47 +1034,37 @@ Route::Pointer UserInputMapper::parseRoute(const QJsonValue& value) {
|
|||
|
||||
const auto& obj = value.toObject();
|
||||
Route::Pointer result = std::make_shared<Route>();
|
||||
result->source = parseEndpoint(obj[JSON_CHANNEL_FROM]);
|
||||
result->source = parseSource(obj[JSON_CHANNEL_FROM]);
|
||||
if (!result->source) {
|
||||
qWarning() << "Invalid route source " << obj[JSON_CHANNEL_FROM];
|
||||
return Route::Pointer();
|
||||
}
|
||||
result->destination = parseEndpoint(obj[JSON_CHANNEL_TO]);
|
||||
|
||||
|
||||
result->destination = parseDestination(obj[JSON_CHANNEL_TO]);
|
||||
if (!result->destination) {
|
||||
qWarning() << "Invalid route destination " << obj[JSON_CHANNEL_TO];
|
||||
return Route::Pointer();
|
||||
}
|
||||
|
||||
if (obj.contains(JSON_CHANNEL_WHEN)) {
|
||||
auto when = parseConditional(obj[JSON_CHANNEL_WHEN]);
|
||||
if (!when) {
|
||||
qWarning() << "Invalid route conditional " << obj[JSON_CHANNEL_TO];
|
||||
auto conditionalsValue = obj[JSON_CHANNEL_WHEN];
|
||||
result->conditional = parseConditional(conditionalsValue);
|
||||
if (!result->conditional) {
|
||||
qWarning() << "Invalid route conditionals " << conditionalsValue;
|
||||
return Route::Pointer();
|
||||
}
|
||||
result->conditional = when;
|
||||
}
|
||||
|
||||
const auto& filtersValue = obj[JSON_CHANNEL_FILTERS];
|
||||
// FIXME support strings for filters with no parameters, both in the array and at the top level...
|
||||
// i.e.
|
||||
// { "from": "Standard.DU", "to" : "Actions.LONGITUDINAL_FORWARD", "filters" : "invert" },
|
||||
// and
|
||||
// { "from": "Standard.DU", "to" : "Actions.LONGITUDINAL_FORWARD", "filters" : [ "invert", "constrainToInteger" ] },
|
||||
if (filtersValue.isArray()) {
|
||||
auto filtersArray = filtersValue.toArray();
|
||||
for (auto filterValue : filtersArray) {
|
||||
if (!filterValue.isObject()) {
|
||||
qWarning() << "Invalid filter " << filterValue;
|
||||
return Route::Pointer();
|
||||
}
|
||||
Filter::Pointer filter = Filter::parse(filterValue.toObject());
|
||||
if (!filter) {
|
||||
qWarning() << "Invalid filter " << filterValue;
|
||||
return Route::Pointer();
|
||||
}
|
||||
result->filters.push_back(filter);
|
||||
if (obj.contains(JSON_CHANNEL_FILTERS)) {
|
||||
auto filtersValue = obj[JSON_CHANNEL_FILTERS];
|
||||
result->filters = parseFilters(filtersValue);
|
||||
if (result->filters.empty()) {
|
||||
qWarning() << "Invalid route filters " << filtersValue;
|
||||
return Route::Pointer();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -917,6 +1111,36 @@ Mapping::Pointer UserInputMapper::parseMapping(const QString& json) {
|
|||
return parseMapping(doc.object());
|
||||
}
|
||||
|
||||
|
||||
void UserInputMapper::enableMapping(const Mapping::Pointer& mapping) {
|
||||
Locker locker(_lock);
|
||||
// New routes for a device get injected IN FRONT of existing routes. Routes
|
||||
// are processed in order so this ensures that the standard -> action processing
|
||||
// takes place after all of the hardware -> standard or hardware -> action processing
|
||||
// because standard -> action is the first set of routes added.
|
||||
for (auto route : mapping->routes) {
|
||||
if (route->source->getInput().device == STANDARD_DEVICE) {
|
||||
_standardRoutes.push_front(route);
|
||||
} else {
|
||||
_deviceRoutes.push_front(route);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UserInputMapper::disableMapping(const Mapping::Pointer& mapping) {
|
||||
Locker locker(_lock);
|
||||
const auto& deviceRoutes = mapping->routes;
|
||||
std::set<Route::Pointer> routeSet(deviceRoutes.begin(), deviceRoutes.end());
|
||||
|
||||
// FIXME this seems to result in empty route pointers... need to find a better way to remove them.
|
||||
std::remove_if(_deviceRoutes.begin(), _deviceRoutes.end(), [&](Route::Pointer route)->bool {
|
||||
return routeSet.count(route) != 0;
|
||||
});
|
||||
std::remove_if(_standardRoutes.begin(), _standardRoutes.end(), [&](Route::Pointer route)->bool {
|
||||
return routeSet.count(route) != 0;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "UserInputMapper.moc"
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <unordered_set>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#include <QtQml/QJSValue>
|
||||
#include <QtScript/QScriptValue>
|
||||
|
@ -50,7 +51,7 @@ namespace controller {
|
|||
using MappingStack = std::list<Mapping::Pointer>;
|
||||
using InputToEndpointMap = std::map<Input, Endpoint::Pointer>;
|
||||
using EndpointSet = std::unordered_set<Endpoint::Pointer>;
|
||||
using ValueMap = std::map<Endpoint::Pointer, float>;
|
||||
using EndpointOverrideMap = std::map<Endpoint::Pointer, Endpoint::Pointer>;
|
||||
using EndpointPair = std::pair<Endpoint::Pointer, Endpoint::Pointer>;
|
||||
using EndpointPairMap = std::map<EndpointPair, Endpoint::Pointer>;
|
||||
using DevicesMap = std::map<int, DeviceProxy::Pointer>;
|
||||
|
@ -86,9 +87,9 @@ namespace controller {
|
|||
int findAction(const QString& actionName) const;
|
||||
QVector<QString> getActionNames() const;
|
||||
|
||||
void setActionState(Action action, float value) { _externalActionStates[toInt(action)] = value; }
|
||||
void deltaActionState(Action action, float delta) { _externalActionStates[toInt(action)] += delta; }
|
||||
void setActionState(Action action, const Pose& value) { _externalPoseStates[toInt(action)] = value; }
|
||||
void setActionState(Action action, float value) { _actionStates[toInt(action)] = value; }
|
||||
void deltaActionState(Action action, float delta) { _actionStates[toInt(action)] += delta; }
|
||||
void setActionState(Action action, const Pose& value) { _poseStates[toInt(action)] = value; }
|
||||
|
||||
static Input makeStandardInput(controller::StandardButtonChannel button);
|
||||
static Input makeStandardInput(controller::StandardAxisChannel axis);
|
||||
|
@ -119,20 +120,16 @@ namespace controller {
|
|||
void hardwareChanged();
|
||||
|
||||
protected:
|
||||
virtual void update();
|
||||
// GetFreeDeviceID should be called before registering a device to use an ID not used by a different device.
|
||||
uint16 getFreeDeviceID() { return _nextFreeDeviceID++; }
|
||||
|
||||
InputDevice::Pointer _standardController;
|
||||
DevicesMap _registeredDevices;
|
||||
uint16 _nextFreeDeviceID = STANDARD_DEVICE + 1;
|
||||
|
||||
std::vector<float> _actionStates = std::vector<float>(toInt(Action::NUM_ACTIONS), 0.0f);
|
||||
std::vector<float> _externalActionStates = std::vector<float>(toInt(Action::NUM_ACTIONS), 0.0f);
|
||||
std::vector<float> _actionScales = std::vector<float>(toInt(Action::NUM_ACTIONS), 1.0f);
|
||||
std::vector<float> _lastActionStates = std::vector<float>(toInt(Action::NUM_ACTIONS), 0.0f);
|
||||
std::vector<Pose> _poseStates = std::vector<Pose>(toInt(Action::NUM_ACTIONS));
|
||||
std::vector<Pose> _externalPoseStates = std::vector<Pose>(toInt(Action::NUM_ACTIONS));
|
||||
|
||||
glm::mat4 _sensorToWorldMat;
|
||||
|
||||
|
@ -144,24 +141,40 @@ namespace controller {
|
|||
|
||||
friend class RouteBuilderProxy;
|
||||
friend class MappingBuilderProxy;
|
||||
|
||||
void runMappings();
|
||||
void applyRoute(const Route::Pointer& route);
|
||||
void enableMapping(const Mapping::Pointer& mapping);
|
||||
void disableMapping(const Mapping::Pointer& mapping);
|
||||
Endpoint::Pointer endpointFor(const QJSValue& endpoint);
|
||||
Endpoint::Pointer endpointFor(const QScriptValue& endpoint);
|
||||
Endpoint::Pointer endpointFor(const Input& endpoint) const;
|
||||
Endpoint::Pointer compositeEndpointFor(Endpoint::Pointer first, Endpoint::Pointer second);
|
||||
|
||||
Mapping::Pointer parseMapping(const QJsonValue& json);
|
||||
Route::Pointer parseRoute(const QJsonValue& value);
|
||||
Conditional::Pointer parseConditional(const QJsonValue& value);
|
||||
Endpoint::Pointer parseDestination(const QJsonValue& value);
|
||||
Endpoint::Pointer parseSource(const QJsonValue& value);
|
||||
Endpoint::Pointer parseEndpoint(const QJsonValue& value);
|
||||
Conditional::Pointer parseConditional(const QJsonValue& value);
|
||||
|
||||
static Filter::Pointer parseFilter(const QJsonValue& value);
|
||||
static Filter::List parseFilters(const QJsonValue& value);
|
||||
|
||||
InputToEndpointMap _endpointsByInput;
|
||||
EndpointToInputMap _inputsByEndpoint;
|
||||
EndpointPairMap _compositeEndpoints;
|
||||
|
||||
ValueMap _overrideValues;
|
||||
EndpointOverrideMap _overrides;
|
||||
MappingNameMap _mappingsByName;
|
||||
Mapping::Pointer _defaultMapping{ std::make_shared<Mapping>("Default") };
|
||||
MappingDeviceMap _mappingsByDevice;
|
||||
MappingStack _activeMappings;
|
||||
|
||||
Route::List _deviceRoutes;
|
||||
Route::List _standardRoutes;
|
||||
|
||||
using Locker = std::unique_lock<std::recursive_mutex>;
|
||||
|
||||
mutable std::recursive_mutex _lock;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -230,7 +230,7 @@ void SixenseManager::update(float deltaTime, bool jointsCaptured) {
|
|||
if (!jointsCaptured) {
|
||||
// Rotation of Palm
|
||||
glm::quat rotation(data->rot_quat[3], data->rot_quat[0], data->rot_quat[1], data->rot_quat[2]);
|
||||
handlePoseEvent(position, rotation, numActiveControllers - 1);
|
||||
handlePoseEvent(position, rotation, left);
|
||||
} else {
|
||||
_poseStateMap.clear();
|
||||
}
|
||||
|
|
|
@ -414,10 +414,10 @@ void ViveControllerManager::buildDeviceProxy(controller::DeviceProxy::Pointer pr
|
|||
|
||||
makePair(LS, "LS"),
|
||||
makePair(RS, "RS"),
|
||||
makePair(LEFT_HAND, "LeftHand"),
|
||||
makePair(RIGHT_HAND, "RightHand"),
|
||||
};
|
||||
|
||||
//availableInputs.append(Input::NamedPair(makeInput(LEFT_HAND), "Left Hand"));
|
||||
|
||||
//availableInputs.append(Input::NamedPair(makeInput(BUTTON_A, 0), "Left Button A"));
|
||||
//availableInputs.append(Input::NamedPair(makeInput(GRIP_BUTTON, 0), "Left Grip Button"));
|
||||
//availableInputs.append(Input::NamedPair(makeInput(TRACKPAD_BUTTON, 0), "Left Trackpad Button"));
|
||||
|
|
Loading…
Reference in a new issue