mirror of
https://github.com/overte-org/overte.git
synced 2025-08-10 17:23:29 +02:00
Wiring up step yaw
This commit is contained in:
parent
81df30d9e5
commit
637654adea
8 changed files with 210 additions and 207 deletions
|
@ -1,4 +1,12 @@
|
||||||
ControllerTest = function() {
|
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");
|
print("Actions");
|
||||||
for (var prop in Controller.Actions) {
|
for (var prop in Controller.Actions) {
|
||||||
|
@ -24,6 +32,9 @@ ControllerTest = function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
ControllerTest.prototype.onCleanup = function() {
|
ControllerTest.prototype.onCleanup = function() {
|
||||||
|
if (this.mappingEnabled) {
|
||||||
|
this.mapping.disable();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,12 +3,12 @@ import QtQuick.Controls 1.0
|
||||||
import QtQuick.Layouts 1.0
|
import QtQuick.Layouts 1.0
|
||||||
import QtQuick.Dialogs 1.0
|
import QtQuick.Dialogs 1.0
|
||||||
|
|
||||||
Item {
|
Rectangle {
|
||||||
id: root
|
id: root
|
||||||
property int size: 64
|
property int size: 64
|
||||||
width: size
|
width: size
|
||||||
height: size
|
height: size
|
||||||
|
color: 'black'
|
||||||
property int controlId: 0
|
property int controlId: 0
|
||||||
property real value: 0.5
|
property real value: 0.5
|
||||||
property int scrollWidth: 1
|
property int scrollWidth: 1
|
||||||
|
@ -16,7 +16,7 @@ Item {
|
||||||
property real max: 1.0
|
property real max: 1.0
|
||||||
property bool log: false
|
property bool log: false
|
||||||
property real range: max - min
|
property real range: max - min
|
||||||
property color color: 'blue'
|
property color lineColor: 'yellow'
|
||||||
property bool bar: false
|
property bool bar: false
|
||||||
property real lastHeight: -1
|
property real lastHeight: -1
|
||||||
property string label: ""
|
property string label: ""
|
||||||
|
@ -49,19 +49,21 @@ Item {
|
||||||
Text {
|
Text {
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
text: root.label
|
text: root.label
|
||||||
|
color: 'white'
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
text: root.max
|
text: root.max
|
||||||
|
color: 'white'
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
text: root.min
|
text: root.min
|
||||||
|
color: 'white'
|
||||||
}
|
}
|
||||||
|
|
||||||
function scroll() {
|
function scroll() {
|
||||||
|
@ -92,7 +94,7 @@ Item {
|
||||||
|
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.lineWidth = 1
|
ctx.lineWidth = 1
|
||||||
ctx.strokeStyle = root.color
|
ctx.strokeStyle = root.lineColor
|
||||||
ctx.moveTo(canvas.width - root.scrollWidth, root.lastHeight).lineTo(canvas.width, currentHeight)
|
ctx.moveTo(canvas.width - root.scrollWidth, root.lastHeight).lineTo(canvas.width, currentHeight)
|
||||||
ctx.stroke()
|
ctx.stroke()
|
||||||
ctx.restore()
|
ctx.restore()
|
||||||
|
|
|
@ -20,8 +20,20 @@ HifiControls.VrDialog {
|
||||||
property var standard: Controller.Standard
|
property var standard: Controller.Standard
|
||||||
property var hydra: null
|
property var hydra: null
|
||||||
property var testMapping: null
|
property var testMapping: null
|
||||||
|
property bool testMappingEnabled: false
|
||||||
property var xbox: null
|
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);
|
||||||
|
testMapping.from(standard.RX).to(actions.StepYaw);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleMapping() {
|
||||||
|
testMapping.enable(!testMappingEnabled);
|
||||||
|
testMappingEnabled = !testMappingEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
enabled = true
|
enabled = true
|
||||||
|
@ -49,110 +61,18 @@ HifiControls.VrDialog {
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
spacing: 8
|
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 {
|
Button {
|
||||||
text: root.xbox ? "XBox Mapping" : "XBox not found"
|
text: !root.testMapping ? "Build Mapping" : (root.testMappingEnabled ? "Disable Mapping" : "Enable Mapping")
|
||||||
property bool built: false
|
|
||||||
enabled: root.xbox && !built
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
var mapping = Controller.newMapping();
|
|
||||||
mapping.from(xbox.A).to(standard.A);
|
if (!root.testMapping) {
|
||||||
mapping.from(xbox.B).to(standard.B);
|
root.buildMapping()
|
||||||
mapping.from(xbox.X).to(standard.X);
|
} else {
|
||||||
mapping.from(xbox.Y).to(standard.Y);
|
root.toggleMapping();
|
||||||
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"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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(standard.RY).invert().to(standard.RY);
|
|
||||||
mapping.makeAxis(standard.LB, standard.RB).to(actions.Yaw);
|
|
||||||
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 {
|
Row {
|
||||||
|
@ -170,25 +90,32 @@ HifiControls.VrDialog {
|
||||||
ScrollingGraph {
|
ScrollingGraph {
|
||||||
controlId: Controller.Actions.Yaw
|
controlId: Controller.Actions.Yaw
|
||||||
label: "Yaw"
|
label: "Yaw"
|
||||||
min: -3.0
|
min: -2.0
|
||||||
max: 3.0
|
max: 2.0
|
||||||
size: 128
|
size: 64
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollingGraph {
|
ScrollingGraph {
|
||||||
controlId: Controller.Actions.YAW_LEFT
|
controlId: Controller.Actions.YawLeft
|
||||||
label: "Yaw Left"
|
label: "Yaw Left"
|
||||||
min: -3.0
|
min: -2.0
|
||||||
max: 3.0
|
max: 2.0
|
||||||
size: 128
|
size: 64
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollingGraph {
|
ScrollingGraph {
|
||||||
controlId: Controller.Actions.YAW_RIGHT
|
controlId: Controller.Actions.YawRight
|
||||||
label: "Yaw Right"
|
label: "Yaw Right"
|
||||||
min: -3.0
|
min: -2.0
|
||||||
max: 3.0
|
max: 2.0
|
||||||
size: 128
|
size: 64
|
||||||
|
}
|
||||||
|
ScrollingGraph {
|
||||||
|
controlId: Controller.Actions.StepYaw
|
||||||
|
label: "StepYaw"
|
||||||
|
min: -2.0
|
||||||
|
max: 2.0
|
||||||
|
size: 64
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ Item {
|
||||||
// Left primary
|
// Left primary
|
||||||
ToggleButton {
|
ToggleButton {
|
||||||
x: parent.width - width; y: parent.height - height;
|
x: parent.width - width; y: parent.height - height;
|
||||||
controlId: root.device.RB
|
controlId: root.device.RightPrimaryThumb
|
||||||
width: 16 * root.scale; height: 16 * root.scale
|
width: 16 * root.scale; height: 16 * root.scale
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,12 @@ namespace controller {
|
||||||
makeAxisPair(Action::ROLL, "Roll"),
|
makeAxisPair(Action::ROLL, "Roll"),
|
||||||
makeAxisPair(Action::PITCH, "Pitch"),
|
makeAxisPair(Action::PITCH, "Pitch"),
|
||||||
makeAxisPair(Action::YAW, "Yaw"),
|
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_BACKWARD, "Backward"),
|
||||||
makeAxisPair(Action::LONGITUDINAL_FORWARD, "Forward"),
|
makeAxisPair(Action::LONGITUDINAL_FORWARD, "Forward"),
|
||||||
makeAxisPair(Action::LATERAL_LEFT, "StrafeLeft"),
|
makeAxisPair(Action::LATERAL_LEFT, "StrafeLeft"),
|
||||||
|
@ -67,7 +73,6 @@ namespace controller {
|
||||||
makeButtonPair(Action::CONTEXT_MENU, "ContextMenu"),
|
makeButtonPair(Action::CONTEXT_MENU, "ContextMenu"),
|
||||||
makeButtonPair(Action::TOGGLE_MUTE, "ToggleMute"),
|
makeButtonPair(Action::TOGGLE_MUTE, "ToggleMute"),
|
||||||
|
|
||||||
|
|
||||||
// Deprecated aliases
|
// Deprecated aliases
|
||||||
// FIXME remove after we port all scripts
|
// FIXME remove after we port all scripts
|
||||||
makeAxisPair(Action::LONGITUDINAL_BACKWARD, "LONGITUDINAL_BACKWARD"),
|
makeAxisPair(Action::LONGITUDINAL_BACKWARD, "LONGITUDINAL_BACKWARD"),
|
||||||
|
|
|
@ -27,6 +27,16 @@ enum class Action {
|
||||||
ROTATE_Y, YAW = ROTATE_Y,
|
ROTATE_Y, YAW = ROTATE_Y,
|
||||||
ROTATE_Z, ROLL = ROTATE_Z,
|
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,
|
TRANSLATE_CAMERA_Z,
|
||||||
NUM_COMBINED_AXES,
|
NUM_COMBINED_AXES,
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,6 @@ namespace controller {
|
||||||
|
|
||||||
// Default contruct allocate the poutput size with the current hardcoded action channels
|
// Default contruct allocate the poutput size with the current hardcoded action channels
|
||||||
controller::UserInputMapper::UserInputMapper() {
|
controller::UserInputMapper::UserInputMapper() {
|
||||||
_activeMappings.push_back(_defaultMapping);
|
|
||||||
_standardController = std::make_shared<StandardController>();
|
_standardController = std::make_shared<StandardController>();
|
||||||
registerDevice(new ActionsDevice());
|
registerDevice(new ActionsDevice());
|
||||||
registerDevice(_standardController.get());
|
registerDevice(_standardController.get());
|
||||||
|
@ -317,6 +316,7 @@ int UserInputMapper::recordDeviceOfType(const QString& deviceName) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void UserInputMapper::registerDevice(InputDevice* device) {
|
void UserInputMapper::registerDevice(InputDevice* device) {
|
||||||
|
Locker locker(_lock);
|
||||||
if (device->_deviceID == Input::INVALID_DEVICE) {
|
if (device->_deviceID == Input::INVALID_DEVICE) {
|
||||||
device->_deviceID = getFreeDeviceID();
|
device->_deviceID = getFreeDeviceID();
|
||||||
}
|
}
|
||||||
|
@ -354,13 +354,7 @@ void UserInputMapper::registerDevice(InputDevice* device) {
|
||||||
auto mapping = loadMapping(device->getDefaultMappingConfig());
|
auto mapping = loadMapping(device->getDefaultMappingConfig());
|
||||||
if (mapping) {
|
if (mapping) {
|
||||||
_mappingsByDevice[deviceID] = mapping;
|
_mappingsByDevice[deviceID] = mapping;
|
||||||
auto& defaultRoutes = _defaultMapping->routes;
|
enableMapping(mapping);
|
||||||
|
|
||||||
// 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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
emit hardwareChanged();
|
emit hardwareChanged();
|
||||||
|
@ -368,6 +362,7 @@ void UserInputMapper::registerDevice(InputDevice* device) {
|
||||||
|
|
||||||
// FIXME remove the associated device mappings
|
// FIXME remove the associated device mappings
|
||||||
void UserInputMapper::removeDevice(int deviceID) {
|
void UserInputMapper::removeDevice(int deviceID) {
|
||||||
|
Locker locker(_lock);
|
||||||
auto proxyEntry = _registeredDevices.find(deviceID);
|
auto proxyEntry = _registeredDevices.find(deviceID);
|
||||||
if (_registeredDevices.end() == proxyEntry) {
|
if (_registeredDevices.end() == proxyEntry) {
|
||||||
qCWarning(controllers) << "Attempted to remove unknown device " << deviceID;
|
qCWarning(controllers) << "Attempted to remove unknown device " << deviceID;
|
||||||
|
@ -376,15 +371,7 @@ void UserInputMapper::removeDevice(int deviceID) {
|
||||||
auto proxy = proxyEntry->second;
|
auto proxy = proxyEntry->second;
|
||||||
auto mappingsEntry = _mappingsByDevice.find(deviceID);
|
auto mappingsEntry = _mappingsByDevice.find(deviceID);
|
||||||
if (_mappingsByDevice.end() != mappingsEntry) {
|
if (_mappingsByDevice.end() != mappingsEntry) {
|
||||||
const auto& mapping = mappingsEntry->second;
|
disableMapping(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;
|
|
||||||
});
|
|
||||||
|
|
||||||
_mappingsByDevice.erase(mappingsEntry);
|
_mappingsByDevice.erase(mappingsEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -395,6 +382,7 @@ void UserInputMapper::removeDevice(int deviceID) {
|
||||||
|
|
||||||
|
|
||||||
DeviceProxy::Pointer UserInputMapper::getDeviceProxy(const Input& input) {
|
DeviceProxy::Pointer UserInputMapper::getDeviceProxy(const Input& input) {
|
||||||
|
Locker locker(_lock);
|
||||||
auto device = _registeredDevices.find(input.getDevice());
|
auto device = _registeredDevices.find(input.getDevice());
|
||||||
if (device != _registeredDevices.end()) {
|
if (device != _registeredDevices.end()) {
|
||||||
return (device->second);
|
return (device->second);
|
||||||
|
@ -404,6 +392,7 @@ DeviceProxy::Pointer UserInputMapper::getDeviceProxy(const Input& input) {
|
||||||
}
|
}
|
||||||
|
|
||||||
QString UserInputMapper::getDeviceName(uint16 deviceID) {
|
QString UserInputMapper::getDeviceName(uint16 deviceID) {
|
||||||
|
Locker locker(_lock);
|
||||||
if (_registeredDevices.find(deviceID) != _registeredDevices.end()) {
|
if (_registeredDevices.find(deviceID) != _registeredDevices.end()) {
|
||||||
return _registeredDevices[deviceID]->_name;
|
return _registeredDevices[deviceID]->_name;
|
||||||
}
|
}
|
||||||
|
@ -411,6 +400,7 @@ QString UserInputMapper::getDeviceName(uint16 deviceID) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int UserInputMapper::findDevice(QString name) const {
|
int UserInputMapper::findDevice(QString name) const {
|
||||||
|
Locker locker(_lock);
|
||||||
for (auto device : _registeredDevices) {
|
for (auto device : _registeredDevices) {
|
||||||
if (device.second->_name == name) {
|
if (device.second->_name == name) {
|
||||||
return device.first;
|
return device.first;
|
||||||
|
@ -420,6 +410,7 @@ int UserInputMapper::findDevice(QString name) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<QString> UserInputMapper::getDeviceNames() {
|
QVector<QString> UserInputMapper::getDeviceNames() {
|
||||||
|
Locker locker(_lock);
|
||||||
QVector<QString> result;
|
QVector<QString> result;
|
||||||
for (auto device : _registeredDevices) {
|
for (auto device : _registeredDevices) {
|
||||||
QString deviceName = device.second->_name.split(" (")[0];
|
QString deviceName = device.second->_name.split(" (")[0];
|
||||||
|
@ -433,6 +424,7 @@ int UserInputMapper::findAction(const QString& actionName) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
Input UserInputMapper::findDeviceInput(const QString& inputName) const {
|
Input UserInputMapper::findDeviceInput(const QString& inputName) const {
|
||||||
|
Locker locker(_lock);
|
||||||
// Split the full input name as such: deviceName.inputName
|
// Split the full input name as such: deviceName.inputName
|
||||||
auto names = inputName.split('.');
|
auto names = inputName.split('.');
|
||||||
|
|
||||||
|
@ -472,6 +464,7 @@ void fixBisectedAxis(float& full, float& negative, float& positive) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void UserInputMapper::update(float deltaTime) {
|
void UserInputMapper::update(float deltaTime) {
|
||||||
|
Locker locker(_lock);
|
||||||
// Reset the axis state for next loop
|
// Reset the axis state for next loop
|
||||||
for (auto& channel : _actionStates) {
|
for (auto& channel : _actionStates) {
|
||||||
channel = 0.0f;
|
channel = 0.0f;
|
||||||
|
@ -505,11 +498,13 @@ void UserInputMapper::update(float deltaTime) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Input::NamedVector UserInputMapper::getAvailableInputs(uint16 deviceID) const {
|
Input::NamedVector UserInputMapper::getAvailableInputs(uint16 deviceID) const {
|
||||||
|
Locker locker(_lock);
|
||||||
auto iterator = _registeredDevices.find(deviceID);
|
auto iterator = _registeredDevices.find(deviceID);
|
||||||
return iterator->second->getAvailabeInputs();
|
return iterator->second->getAvailabeInputs();
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<Action> UserInputMapper::getAllActions() const {
|
QVector<Action> UserInputMapper::getAllActions() const {
|
||||||
|
Locker locker(_lock);
|
||||||
QVector<Action> actions;
|
QVector<Action> actions;
|
||||||
for (auto i = 0; i < toInt(Action::NUM_ACTIONS); i++) {
|
for (auto i = 0; i < toInt(Action::NUM_ACTIONS); i++) {
|
||||||
actions.append(Action(i));
|
actions.append(Action(i));
|
||||||
|
@ -518,6 +513,7 @@ QVector<Action> UserInputMapper::getAllActions() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
QString UserInputMapper::getActionName(Action action) const {
|
QString UserInputMapper::getActionName(Action action) const {
|
||||||
|
Locker locker(_lock);
|
||||||
for (auto actionPair : getActionInputs()) {
|
for (auto actionPair : getActionInputs()) {
|
||||||
if (actionPair.first.channel == toInt(action)) {
|
if (actionPair.first.channel == toInt(action)) {
|
||||||
return actionPair.second;
|
return actionPair.second;
|
||||||
|
@ -528,6 +524,7 @@ QString UserInputMapper::getActionName(Action action) const {
|
||||||
|
|
||||||
|
|
||||||
QVector<QString> UserInputMapper::getActionNames() const {
|
QVector<QString> UserInputMapper::getActionNames() const {
|
||||||
|
Locker locker(_lock);
|
||||||
QVector<QString> result;
|
QVector<QString> result;
|
||||||
for (auto actionPair : getActionInputs()) {
|
for (auto actionPair : getActionInputs()) {
|
||||||
result << actionPair.second;
|
result << actionPair.second;
|
||||||
|
@ -645,74 +642,87 @@ void UserInputMapper::runMappings() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now process the current values for each level of the stack
|
// Now process the current values for each level of the stack
|
||||||
for (auto& mapping : _activeMappings) {
|
for (const auto& route : _deviceRoutes) {
|
||||||
for (const auto& route : mapping->routes) {
|
if (!route) {
|
||||||
if (route->conditional) {
|
continue;
|
||||||
if (!route->conditional->satisfied()) {
|
}
|
||||||
continue;
|
applyRoute(route);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
auto source = route->source;
|
for (const auto& route : _standardRoutes) {
|
||||||
if (_overrides.count(source)) {
|
if (!route) {
|
||||||
source = _overrides[source];
|
continue;
|
||||||
}
|
}
|
||||||
|
applyRoute(route);
|
||||||
|
}
|
||||||
|
|
||||||
// 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()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
auto input = source->getInput();
|
void UserInputMapper::applyRoute(const Route::Pointer& route) {
|
||||||
float value = source->value();
|
if (route->conditional) {
|
||||||
if (value != 0.0) {
|
if (!route->conditional->satisfied()) {
|
||||||
int i = 0;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME?, should come before or after the override logic?
|
|
||||||
if (!destination->writeable()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Endpoint::Pointer UserInputMapper::endpointFor(const QJSValue& endpoint) {
|
Endpoint::Pointer UserInputMapper::endpointFor(const QJSValue& endpoint) {
|
||||||
|
@ -744,6 +754,7 @@ Endpoint::Pointer UserInputMapper::endpointFor(const QScriptValue& endpoint) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Endpoint::Pointer UserInputMapper::endpointFor(const Input& inputId) const {
|
Endpoint::Pointer UserInputMapper::endpointFor(const Input& inputId) const {
|
||||||
|
Locker locker(_lock);
|
||||||
auto iterator = _endpointsByInput.find(inputId);
|
auto iterator = _endpointsByInput.find(inputId);
|
||||||
if (_endpointsByInput.end() == iterator) {
|
if (_endpointsByInput.end() == iterator) {
|
||||||
qWarning() << "Unknown input: " << QString::number(inputId.getID(), 16);
|
qWarning() << "Unknown input: " << QString::number(inputId.getID(), 16);
|
||||||
|
@ -767,6 +778,7 @@ Endpoint::Pointer UserInputMapper::compositeEndpointFor(Endpoint::Pointer first,
|
||||||
|
|
||||||
|
|
||||||
Mapping::Pointer UserInputMapper::newMapping(const QString& mappingName) {
|
Mapping::Pointer UserInputMapper::newMapping(const QString& mappingName) {
|
||||||
|
Locker locker(_lock);
|
||||||
if (_mappingsByName.count(mappingName)) {
|
if (_mappingsByName.count(mappingName)) {
|
||||||
qCWarning(controllers) << "Refusing to recreate mapping named " << mappingName;
|
qCWarning(controllers) << "Refusing to recreate mapping named " << mappingName;
|
||||||
}
|
}
|
||||||
|
@ -799,8 +811,8 @@ Mapping::Pointer UserInputMapper::newMapping(const QString& mappingName) {
|
||||||
// return result;
|
// return result;
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
|
||||||
void UserInputMapper::enableMapping(const QString& mappingName, bool enable) {
|
void UserInputMapper::enableMapping(const QString& mappingName, bool enable) {
|
||||||
|
Locker locker(_lock);
|
||||||
qCDebug(controllers) << "Attempting to enable mapping " << mappingName;
|
qCDebug(controllers) << "Attempting to enable mapping " << mappingName;
|
||||||
auto iterator = _mappingsByName.find(mappingName);
|
auto iterator = _mappingsByName.find(mappingName);
|
||||||
if (_mappingsByName.end() == iterator) {
|
if (_mappingsByName.end() == iterator) {
|
||||||
|
@ -810,18 +822,14 @@ void UserInputMapper::enableMapping(const QString& mappingName, bool enable) {
|
||||||
|
|
||||||
auto mapping = iterator->second;
|
auto mapping = iterator->second;
|
||||||
if (enable) {
|
if (enable) {
|
||||||
_activeMappings.push_front(mapping);
|
enableMapping(mapping);
|
||||||
} else {
|
} else {
|
||||||
auto activeIterator = std::find(_activeMappings.begin(), _activeMappings.end(), mapping);
|
disableMapping(mapping);
|
||||||
if (_activeMappings.end() == activeIterator) {
|
|
||||||
qCWarning(controllers) << "Attempted to disable inactive mapping " << mappingName;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_activeMappings.erase(activeIterator);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float UserInputMapper::getValue(const Endpoint::Pointer& endpoint) const {
|
float UserInputMapper::getValue(const Endpoint::Pointer& endpoint) const {
|
||||||
|
Locker locker(_lock);
|
||||||
auto valuesIterator = _overrides.find(endpoint);
|
auto valuesIterator = _overrides.find(endpoint);
|
||||||
if (_overrides.end() != valuesIterator) {
|
if (_overrides.end() != valuesIterator) {
|
||||||
return valuesIterator->second->value();
|
return valuesIterator->second->value();
|
||||||
|
@ -854,6 +862,7 @@ Pose UserInputMapper::getPose(const Input& input) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
Mapping::Pointer UserInputMapper::loadMapping(const QString& jsonFile) {
|
Mapping::Pointer UserInputMapper::loadMapping(const QString& jsonFile) {
|
||||||
|
Locker locker(_lock);
|
||||||
if (jsonFile.isEmpty()) {
|
if (jsonFile.isEmpty()) {
|
||||||
return Mapping::Pointer();
|
return Mapping::Pointer();
|
||||||
}
|
}
|
||||||
|
@ -1102,6 +1111,36 @@ Mapping::Pointer UserInputMapper::parseMapping(const QString& json) {
|
||||||
return parseMapping(doc.object());
|
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"
|
#include "UserInputMapper.moc"
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
#include <QtQml/QJSValue>
|
#include <QtQml/QJSValue>
|
||||||
#include <QtScript/QScriptValue>
|
#include <QtScript/QScriptValue>
|
||||||
|
@ -119,10 +120,8 @@ namespace controller {
|
||||||
void hardwareChanged();
|
void hardwareChanged();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void runMappings();
|
|
||||||
// GetFreeDeviceID should be called before registering a device to use an ID not used by a different device.
|
// GetFreeDeviceID should be called before registering a device to use an ID not used by a different device.
|
||||||
uint16 getFreeDeviceID() { return _nextFreeDeviceID++; }
|
uint16 getFreeDeviceID() { return _nextFreeDeviceID++; }
|
||||||
|
|
||||||
InputDevice::Pointer _standardController;
|
InputDevice::Pointer _standardController;
|
||||||
DevicesMap _registeredDevices;
|
DevicesMap _registeredDevices;
|
||||||
uint16 _nextFreeDeviceID = STANDARD_DEVICE + 1;
|
uint16 _nextFreeDeviceID = STANDARD_DEVICE + 1;
|
||||||
|
@ -142,6 +141,11 @@ namespace controller {
|
||||||
|
|
||||||
friend class RouteBuilderProxy;
|
friend class RouteBuilderProxy;
|
||||||
friend class MappingBuilderProxy;
|
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 QJSValue& endpoint);
|
||||||
Endpoint::Pointer endpointFor(const QScriptValue& endpoint);
|
Endpoint::Pointer endpointFor(const QScriptValue& endpoint);
|
||||||
Endpoint::Pointer endpointFor(const Input& endpoint) const;
|
Endpoint::Pointer endpointFor(const Input& endpoint) const;
|
||||||
|
@ -163,9 +167,14 @@ namespace controller {
|
||||||
|
|
||||||
EndpointOverrideMap _overrides;
|
EndpointOverrideMap _overrides;
|
||||||
MappingNameMap _mappingsByName;
|
MappingNameMap _mappingsByName;
|
||||||
Mapping::Pointer _defaultMapping{ std::make_shared<Mapping>("Default") };
|
|
||||||
MappingDeviceMap _mappingsByDevice;
|
MappingDeviceMap _mappingsByDevice;
|
||||||
MappingStack _activeMappings;
|
|
||||||
|
Route::List _deviceRoutes;
|
||||||
|
Route::List _standardRoutes;
|
||||||
|
|
||||||
|
using Locker = std::unique_lock<std::recursive_mutex>;
|
||||||
|
|
||||||
|
mutable std::recursive_mutex _lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue