Working on JSON parsing and cleanup

Conflicts:
	libraries/controllers/src/controllers/Route.cpp
	libraries/controllers/src/controllers/ScriptingInterface.cpp
	libraries/controllers/src/controllers/StandardController.cpp
	libraries/controllers/src/controllers/impl/RouteBuilderProxy.h
This commit is contained in:
Brad Davis 2015-10-19 19:31:25 -07:00
parent ef6c4f6f66
commit bea6fdd890
49 changed files with 1925 additions and 2116 deletions

View file

@ -0,0 +1,25 @@
{
"name": "Keyboard/Mouse to Actions",
"channels": [
{ "from": "Keyboard.W", "to": "Actions.LONGITUDINAL_FORWARD" },
{ "from": "Keyboard.S", "to": "Actions.LONGITUDINAL_BACKWARD" },
{ "from": "Keyboard.A", "to": "Actions.YAW_LEFT" },
{ "from": "Keyboard.D", "to": "Actions.YAW_RIGHT" },
{ "from": "Keyboard.A", "when": "Keyboard.Shift", "to": "Actions.LATERAL_LEFT" },
{ "from": "Keyboard.D", "when": "Keyboard.Shift", "to": "Actions.LATERAL_RIGHT" },
{ "from": "Keyboard.A", "when": "Keyboard.RightMouseClick", "to": "Actions.LATERAL_LEFT" },
{ "from": "Keyboard.D", "when": "Keyboard.RightMouseClick", "to": "Actions.LATERAL_RIGHT" },
{ "from": "Keyboard.C", "to": "Actions.VERTICAL_DOWN" },
{ "from": "Keyboard.E", "to": "Actions.VERTICAL_UP" },
{ "from": "Keyboard.Up", "to": "Actions.LONGITUDINAL_FORWARD" },
{ "from": "Keyboard.Down", "to": "Actions.LONGITUDINAL_BACKWARD" },
{ "from": "Keyboard.Left", "to": "Actions.YAW_LEFT" },
{ "from": "Keyboard.Right", "to": "Actions.YAW_RIGHT" },
{ "from": "Keyboard.PgDown", "to": "Actions.VERTICAL_DOWN" },
{ "from": "Keyboard.PgUp", "to": "Actions.VERTICAL_UP" },
{ "from": "Keyboard.MouseMoveLeft", "when": "Keyboard.RightMouseClick", "to": "Actions.YAW_LEFT" },
{ "from": "Keyboard.MouseMoveRight", "when": "Keyboard.RightMouseClick", "to": "Actions.YAW_RIGHT" },
{ "from": "Keyboard.MouseMoveUp", "when": "Keyboard.RightMouseClick", "to": "Actions.PITCH_UP" },
{ "from": "Keyboard.MouseMoveDown", "when": "Keyboard.RightMouseClick", "to": "Actions.PITCH_DOWN" }
]
}

View file

@ -0,0 +1,41 @@
{
"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 } ]
}
]
}

View file

@ -1,29 +1,29 @@
{
"name": "XBox to Standard",
"channels": [
{ "from": "XBox.LY", "to": "Standard.LY" },
{ "from": "XBox.LX", "to": "Standard.LX" },
{ "from": "XBox.LT", "to": "Standard.LT" },
{ "from": "XBox.LB", "to": "Standard.LB" },
{ "from": "XBox.LS", "to": "Standard.LS" },
{ "from": "GamePad.LY", "to": "Standard.LY" },
{ "from": "GamePad.LX", "to": "Standard.LX" },
{ "from": "GamePad.LT", "to": "Standard.LT" },
{ "from": "GamePad.LB", "to": "Standard.LB" },
{ "from": "GamePad.LS", "to": "Standard.LS" },
{ "from": "XBox.RY", "to": "Standard.RY" },
{ "from": "XBox.RX", "to": "Standard.RX" },
{ "from": "XBox.RT", "to": "Standard.RT" },
{ "from": "XBox.RB", "to": "Standard.RB" },
{ "from": "XBox.RS", "to": "Standard.RS" },
{ "from": "GamePad.RY", "to": "Standard.RY" },
{ "from": "GamePad.RX", "to": "Standard.RX" },
{ "from": "GamePad.RT", "to": "Standard.RT" },
{ "from": "GamePad.RB", "to": "Standard.RB" },
{ "from": "GamePad.RS", "to": "Standard.RS" },
{ "from": "XBox.Back", "to": "Standard.Back" },
{ "from": "XBox.Start", "to": "Standard.Start" },
{ "from": "GamePad.Back", "to": "Standard.Back" },
{ "from": "GamePad.Start", "to": "Standard.Start" },
{ "from": "XBox.DU", "to": "Standard.DU" },
{ "from": "XBox.DD", "to": "Standard.DD" },
{ "from": "XBox.DL", "to": "Standard.DL" },
{ "from": "XBox.DR", "to": "Standard.DR" },
{ "from": "GamePad.DU", "to": "Standard.DU" },
{ "from": "GamePad.DD", "to": "Standard.DD" },
{ "from": "GamePad.DL", "to": "Standard.DL" },
{ "from": "GamePad.DR", "to": "Standard.DR" },
{ "from": "XBox.A", "to": "Standard.A" },
{ "from": "XBox.B", "to": "Standard.B" },
{ "from": "XBox.X", "to": "Standard.X" },
{ "from": "XBox.Y", "to": "Standard.Y" }
{ "from": "GamePad.A", "to": "Standard.A" },
{ "from": "GamePad.B", "to": "Standard.B" },
{ "from": "GamePad.X", "to": "Standard.X" },
{ "from": "GamePad.Y", "to": "Standard.Y" }
]
}

View file

@ -25,7 +25,7 @@ HifiControls.VrDialog {
Component.onCompleted: {
enabled = true
var xboxRegex = /^X360Controller/;
var xboxRegex = /^GamePad/;
var hydraRegex = /^Hydra/;
for (var prop in Controller.Hardware) {
if(xboxRegex.test(prop)) {

View file

@ -631,7 +631,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
connect(userInputMapper.data(), &UserInputMapper::actionEvent, [this](int action, float state) {
if (state) {
switch (action) {
case UserInputMapper::Action::TOGGLE_MUTE:
case controller::Action::TOGGLE_MUTE:
DependencyManager::get<AudioClient>()->toggleMute();
break;
}
@ -639,8 +639,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
});
// Setup the keyboardMouseDevice and the user input mapper with the default bindings
_keyboardMouseDevice->registerToUserInputMapper(*userInputMapper);
_keyboardMouseDevice->assignDefaultInputMapping(*userInputMapper);
userInputMapper->registerDevice(_keyboardMouseDevice);
// check first run...
if (_firstRun.get()) {
@ -2182,7 +2181,7 @@ float Application::getAvatarSimrate() {
}
void Application::setLowVelocityFilter(bool lowVelocityFilter) {
InputDevice::setLowVelocityFilter(lowVelocityFilter);
controller::InputDevice::setLowVelocityFilter(lowVelocityFilter);
}
ivec2 Application::getMouse() const {
@ -2710,19 +2709,28 @@ void Application::update(float deltaTime) {
auto myAvatar = getMyAvatar();
auto userInputMapper = DependencyManager::get<UserInputMapper>();
userInputMapper->setSensorToWorldMat(myAvatar->getSensorToWorldMatrix());
// Dispatch input events
_controllerScriptingInterface->update();
userInputMapper->update(deltaTime);
bool jointsCaptured = false;
for (auto inputPlugin : PluginManager::getInstance()->getInputPlugins()) {
if (inputPlugin->isActive()) {
inputPlugin->pluginUpdate(deltaTime, jointsCaptured);
if (inputPlugin->isJointController()) {
jointsCaptured = true;
}
}
}
// Transfer the user inputs to the driveKeys
myAvatar->clearDriveKeys();
if (_myCamera.getMode() != CAMERA_MODE_INDEPENDENT) {
if (!_controllerScriptingInterface->areActionsCaptured()) {
myAvatar->setDriveKeys(FWD, userInputMapper->getActionState(UserInputMapper::LONGITUDINAL_FORWARD));
myAvatar->setDriveKeys(BACK, userInputMapper->getActionState(UserInputMapper::LONGITUDINAL_BACKWARD));
myAvatar->setDriveKeys(UP, userInputMapper->getActionState(UserInputMapper::VERTICAL_UP));
myAvatar->setDriveKeys(DOWN, userInputMapper->getActionState(UserInputMapper::VERTICAL_DOWN));
myAvatar->setDriveKeys(LEFT, userInputMapper->getActionState(UserInputMapper::LATERAL_LEFT));
myAvatar->setDriveKeys(RIGHT, userInputMapper->getActionState(UserInputMapper::LATERAL_RIGHT));
myAvatar->setDriveKeys(FWD, userInputMapper->getActionState(controller::LONGITUDINAL_FORWARD));
myAvatar->setDriveKeys(BACK, userInputMapper->getActionState(controller::LONGITUDINAL_BACKWARD));
myAvatar->setDriveKeys(UP, userInputMapper->getActionState(controller::VERTICAL_UP));
myAvatar->setDriveKeys(DOWN, userInputMapper->getActionState(controller::VERTICAL_DOWN));
myAvatar->setDriveKeys(LEFT, userInputMapper->getActionState(controller::LATERAL_LEFT));
myAvatar->setDriveKeys(RIGHT, userInputMapper->getActionState(controller::LATERAL_RIGHT));
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.
@ -2730,25 +2738,25 @@ 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(UserInputMapper::PITCH_UP) / timeFactor);
myAvatar->setDriveKeys(ROT_DOWN, userInputMapper->getActionState(UserInputMapper::PITCH_DOWN) / timeFactor);
myAvatar->setDriveKeys(ROT_LEFT, userInputMapper->getActionState(UserInputMapper::YAW_LEFT) / timeFactor);
myAvatar->setDriveKeys(ROT_RIGHT, userInputMapper->getActionState(UserInputMapper::YAW_RIGHT) / timeFactor);
myAvatar->setDriveKeys(ROT_UP, userInputMapper->getActionState(controller::PITCH_UP) / timeFactor);
myAvatar->setDriveKeys(ROT_DOWN, userInputMapper->getActionState(controller::PITCH_DOWN) / timeFactor);
myAvatar->setDriveKeys(ROT_LEFT, userInputMapper->getActionState(controller::YAW_LEFT) / timeFactor);
myAvatar->setDriveKeys(ROT_RIGHT, userInputMapper->getActionState(controller::YAW_RIGHT) / timeFactor);
}
}
myAvatar->setDriveKeys(BOOM_IN, userInputMapper->getActionState(UserInputMapper::BOOM_IN));
myAvatar->setDriveKeys(BOOM_OUT, userInputMapper->getActionState(UserInputMapper::BOOM_OUT));
myAvatar->setDriveKeys(BOOM_IN, userInputMapper->getActionState(controller::BOOM_IN));
myAvatar->setDriveKeys(BOOM_OUT, userInputMapper->getActionState(controller::BOOM_OUT));
}
UserInputMapper::PoseValue leftHand = userInputMapper->getPoseState(UserInputMapper::LEFT_HAND);
UserInputMapper::PoseValue rightHand = userInputMapper->getPoseState(UserInputMapper::RIGHT_HAND);
controller::Pose leftHand = userInputMapper->getPoseState(controller::LEFT_HAND);
controller::Pose rightHand = userInputMapper->getPoseState(controller::RIGHT_HAND);
Hand* hand = DependencyManager::get<AvatarManager>()->getMyAvatar()->getHand();
setPalmData(hand, leftHand, deltaTime, LEFT_HAND_INDEX, userInputMapper->getActionState(UserInputMapper::LEFT_HAND_CLICK));
setPalmData(hand, rightHand, deltaTime, RIGHT_HAND_INDEX, userInputMapper->getActionState(UserInputMapper::RIGHT_HAND_CLICK));
setPalmData(hand, leftHand, deltaTime, LEFT_HAND_INDEX, userInputMapper->getActionState(controller::LEFT_HAND_CLICK));
setPalmData(hand, rightHand, deltaTime, RIGHT_HAND_INDEX, userInputMapper->getActionState(controller::RIGHT_HAND_CLICK));
if (Menu::getInstance()->isOptionChecked(MenuOption::EnableHandMouseInput)) {
emulateMouse(hand, userInputMapper->getActionState(UserInputMapper::LEFT_HAND_CLICK),
userInputMapper->getActionState(UserInputMapper::SHIFT), LEFT_HAND_INDEX);
emulateMouse(hand, userInputMapper->getActionState(UserInputMapper::RIGHT_HAND_CLICK),
userInputMapper->getActionState(UserInputMapper::SHIFT), RIGHT_HAND_INDEX);
emulateMouse(hand, userInputMapper->getActionState(controller::LEFT_HAND_CLICK),
userInputMapper->getActionState(controller::SHIFT), LEFT_HAND_INDEX);
emulateMouse(hand, userInputMapper->getActionState(controller::RIGHT_HAND_CLICK),
userInputMapper->getActionState(controller::SHIFT), RIGHT_HAND_INDEX);
}
updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process...
@ -4806,7 +4814,7 @@ mat4 Application::getHMDSensorPose() const {
return mat4();
}
void Application::setPalmData(Hand* hand, UserInputMapper::PoseValue pose, float deltaTime, int index, float triggerValue) {
void Application::setPalmData(Hand* hand, const controller::Pose& pose, float deltaTime, int index, float triggerValue) {
PalmData* palm;
bool foundHand = false;
for (size_t j = 0; j < hand->getNumPalms(); j++) {
@ -4855,7 +4863,7 @@ void Application::setPalmData(Hand* hand, UserInputMapper::PoseValue pose, float
palm->setRawAngularVelocity(glm::vec3(0.0f));
}
if (InputDevice::getLowVelocityFilter()) {
if (controller::InputDevice::getLowVelocityFilter()) {
// Use a velocity sensitive filter to damp small motions and preserve large ones with
// no latency.
float velocityFilter = glm::clamp(1.0f - glm::length(rawVelocity), 0.0f, 1.0f);
@ -4911,7 +4919,7 @@ void Application::emulateMouse(Hand* hand, float click, float shift, int index)
float yAngle = 0.5f - ((atan2f(direction.z, direction.y) + (float)M_PI_2));
auto canvasSize = getCanvasSize();
// Get the pixel range over which the xAngle and yAngle are scaled
float cursorRange = canvasSize.x * InputDevice::getCursorPixelRangeMult();
float cursorRange = canvasSize.x * controller::InputDevice::getCursorPixelRangeMult();
pos.setX(canvasSize.x / 2.0f + cursorRange * xAngle);
pos.setY(canvasSize.y / 2.0f + cursorRange * yAngle);

View file

@ -352,7 +352,7 @@ private:
void update(float deltaTime);
void setPalmData(Hand* hand, UserInputMapper::PoseValue pose, float deltaTime, int index, float triggerValue);
void setPalmData(Hand* hand, const controller::Pose& pose, float deltaTime, int index, float triggerValue);
void emulateMouse(Hand* hand, float click, float shift, int index);
// Various helper functions called during update()

View file

@ -10,8 +10,11 @@
//
#include "3DConnexionClient.h"
#include <UserActivityLogger.h>
#include <PathUtils.h>
#include "Menu.h"
#include "UserActivityLogger.h"
const float MAX_AXIS = 75.0f; // max forward = 2x speed
@ -25,8 +28,9 @@ ConnexionData& ConnexionData::getInstance() {
return sharedInstance;
}
ConnexionData::ConnexionData() {
}
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;
@ -48,76 +52,71 @@ void ConnexionData::setButton(int lastButtonState) {
_buttonPressedMap.insert(lastButtonState);
}
void ConnexionData::registerToUserInputMapper(UserInputMapper& mapper) {
// Grab the current free device ID
_deviceID = mapper.getFreeDeviceID();
void ConnexionData::buildDeviceProxy(controller::DeviceProxy::Pointer proxy) {
proxy->_name = _name = "ConnexionClient";
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;
auto proxy = UserInputMapper::DeviceProxy::Pointer(new UserInputMapper::DeviceProxy("ConnexionClient"));
proxy->getButton = [this](const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); };
proxy->getAxis = [this](const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); };
proxy->getAvailabeInputs = [this]() -> QVector<UserInputMapper::InputPair> {
QVector<UserInputMapper::InputPair> availableInputs;
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_1), "Left button"));
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_2), "Right button"));
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_3), "Both buttons"));
availableInputs.append(UserInputMapper::InputPair(makeInput(POSITION_AXIS_Y_NEG), "Move backward"));
availableInputs.append(UserInputMapper::InputPair(makeInput(POSITION_AXIS_Y_POS), "Move forward"));
availableInputs.append(UserInputMapper::InputPair(makeInput(POSITION_AXIS_X_POS), "Move right"));
availableInputs.append(UserInputMapper::InputPair(makeInput(POSITION_AXIS_X_NEG), "Move Left"));
availableInputs.append(UserInputMapper::InputPair(makeInput(POSITION_AXIS_Z_POS), "Move up"));
availableInputs.append(UserInputMapper::InputPair(makeInput(POSITION_AXIS_Z_NEG), "Move down"));
availableInputs.append(UserInputMapper::InputPair(makeInput(ROTATION_AXIS_Y_NEG), "Rotate backward"));
availableInputs.append(UserInputMapper::InputPair(makeInput(ROTATION_AXIS_Y_POS), "Rotate forward"));
availableInputs.append(UserInputMapper::InputPair(makeInput(ROTATION_AXIS_X_POS), "Rotate right"));
availableInputs.append(UserInputMapper::InputPair(makeInput(ROTATION_AXIS_X_NEG), "Rotate left"));
availableInputs.append(UserInputMapper::InputPair(makeInput(ROTATION_AXIS_Z_POS), "Rotate up"));
availableInputs.append(UserInputMapper::InputPair(makeInput(ROTATION_AXIS_Z_NEG), "Rotate down"));
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"));
return availableInputs;
};
proxy->resetDeviceBindings = [this, &mapper]() -> bool {
mapper.removeAllInputChannelsForDevice(_deviceID);
this->assignDefaultInputMapping(mapper);
return true;
};
mapper.registerDevice(_deviceID, proxy);
}
void ConnexionData::assignDefaultInputMapping(UserInputMapper& mapper) {
const float JOYSTICK_MOVE_SPEED = 1.0f;
//const float DPAD_MOVE_SPEED = 0.5f;
const float JOYSTICK_YAW_SPEED = 0.5f;
const float JOYSTICK_PITCH_SPEED = 0.25f;
const float BOOM_SPEED = 0.1f;
// Y axes are flipped (up is negative)
// postion: Movement, strafing
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(POSITION_AXIS_Y_NEG), JOYSTICK_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(POSITION_AXIS_Y_POS), JOYSTICK_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(POSITION_AXIS_X_POS), JOYSTICK_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(POSITION_AXIS_X_NEG), JOYSTICK_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(POSITION_AXIS_Z_NEG), JOYSTICK_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(POSITION_AXIS_Z_POS), JOYSTICK_MOVE_SPEED);
// Rotation: Camera orientation with button 1
mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(ROTATION_AXIS_Z_POS), JOYSTICK_YAW_SPEED);
mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(ROTATION_AXIS_Z_NEG), JOYSTICK_YAW_SPEED);
mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(ROTATION_AXIS_Y_NEG), JOYSTICK_PITCH_SPEED);
mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(ROTATION_AXIS_Y_POS), JOYSTICK_PITCH_SPEED);
// Button controls
// Zoom
mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(BUTTON_1), BOOM_SPEED);
mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(BUTTON_2), BOOM_SPEED);
// Zoom
// mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(ROTATION_AXIS_Z_NEG), BOOM_SPEED);
// mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(ROTATION_AXIS_Z_POS), BOOM_SPEED);
QString ConnexionData::getDefaultMappingConfig() {
static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/vive.json";
return MAPPING_JSON;
}
//void ConnexionData::assignDefaultInputMapping(UserInputMapper& mapper) {
// const float JOYSTICK_MOVE_SPEED = 1.0f;
// //const float DPAD_MOVE_SPEED = 0.5f;
// const float JOYSTICK_YAW_SPEED = 0.5f;
// const float JOYSTICK_PITCH_SPEED = 0.25f;
// const float BOOM_SPEED = 0.1f;
//
// // Y axes are flipped (up is negative)
// // postion: Movement, strafing
// mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(POSITION_AXIS_Y_NEG), JOYSTICK_MOVE_SPEED);
// mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(POSITION_AXIS_Y_POS), JOYSTICK_MOVE_SPEED);
// mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(POSITION_AXIS_X_POS), JOYSTICK_MOVE_SPEED);
// mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(POSITION_AXIS_X_NEG), JOYSTICK_MOVE_SPEED);
// mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(POSITION_AXIS_Z_NEG), JOYSTICK_MOVE_SPEED);
// mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(POSITION_AXIS_Z_POS), JOYSTICK_MOVE_SPEED);
//
// // Rotation: Camera orientation with button 1
// mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(ROTATION_AXIS_Z_POS), JOYSTICK_YAW_SPEED);
// mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(ROTATION_AXIS_Z_NEG), JOYSTICK_YAW_SPEED);
// mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(ROTATION_AXIS_Y_NEG), JOYSTICK_PITCH_SPEED);
// mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(ROTATION_AXIS_Y_POS), JOYSTICK_PITCH_SPEED);
//
// // Button controls
// // Zoom
// mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(BUTTON_1), BOOM_SPEED);
// mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(BUTTON_2), BOOM_SPEED);
//
// // Zoom
// // mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(ROTATION_AXIS_Z_NEG), BOOM_SPEED);
// // mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(ROTATION_AXIS_Z_POS), BOOM_SPEED);
//
//}
float ConnexionData::getButton(int channel) const {
if (!_buttonPressedMap.empty()) {
if (_buttonPressedMap.find(channel) != _buttonPressedMap.end()) {
@ -138,15 +137,15 @@ float ConnexionData::getAxis(int channel) const {
}
}
UserInputMapper::Input ConnexionData::makeInput(ConnexionData::ButtonChannel button) {
return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON);
controller::Input ConnexionData::makeInput(ConnexionData::ButtonChannel button) {
return controller::Input(_deviceID, button, controller::ChannelType::BUTTON);
}
UserInputMapper::Input ConnexionData::makeInput(ConnexionData::PositionChannel axis) {
return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS);
controller::Input ConnexionData::makeInput(ConnexionData::PositionChannel axis) {
return controller::Input(_deviceID, axis, controller::ChannelType::AXIS);
}
void ConnexionData::update() {
void ConnexionData::update(float deltaTime, bool jointsCaptured) {
// the update is done in the ConnexionClient class.
// for windows in the nativeEventFilter the inputmapper is connected or registed or removed when an 3Dconnnexion device is attached or detached
// for osx the api will call DeviceAddedHandler or DeviceRemoveHandler when a 3Dconnexion device is attached or detached
@ -187,7 +186,6 @@ void ConnexionClient::destroy() {
QAbstractEventDispatcher::instance()->removeNativeEventFilter(this);
ConnexionData& connexiondata = ConnexionData::getInstance();
int deviceid = connexiondata.getDeviceID();
connexiondata.setDeviceID(0);
auto userInputMapper = DependencyManager::get<UserInputMapper>();
userInputMapper->removeDevice(deviceid);
}
@ -295,13 +293,10 @@ bool ConnexionClient::RawInputEventFilter(void* msg, long* result) {
ConnexionData& connexiondata = ConnexionData::getInstance();
auto userInputMapper = DependencyManager::get<UserInputMapper>();
if (Is3dmouseAttached() && connexiondata.getDeviceID() == 0) {
connexiondata.registerToUserInputMapper(*userInputMapper);
connexiondata.assignDefaultInputMapping(*userInputMapper);
userInputMapper->registerDevice(&connexiondata);
UserActivityLogger::getInstance().connectedDevice("controller", "3Dconnexion");
} else if (!Is3dmouseAttached() && connexiondata.getDeviceID() != 0) {
int deviceid = connexiondata.getDeviceID();
connexiondata.setDeviceID(0);
userInputMapper->removeDevice(deviceid);
userInputMapper->removeDevice(connexiondata.getDeviceID());
}
if (!Is3dmouseAttached()) {
@ -894,8 +889,7 @@ void ConnexionClient::init() {
if (Is3dmouseAttached() && connexiondata.getDeviceID() == 0) {
auto userInputMapper = DependencyManager::get<UserInputMapper>();
connexiondata.registerToUserInputMapper(*userInputMapper);
connexiondata.assignDefaultInputMapping(*userInputMapper);
userInputMapper->registerDevice(&connexiondata);
UserActivityLogger::getInstance().connectedDevice("controller", "3Dconnexion");
}
//let one axis be dominant
@ -926,8 +920,7 @@ void DeviceAddedHandler(unsigned int connection) {
if (connexiondata.getDeviceID() == 0) {
qCWarning(interfaceapp) << "3Dconnexion device added ";
auto userInputMapper = DependencyManager::get<UserInputMapper>();
connexiondata.registerToUserInputMapper(*userInputMapper);
connexiondata.assignDefaultInputMapping(*userInputMapper);
userInputMapper->registerDevice(&connexiondata);
UserActivityLogger::getInstance().connectedDevice("controller", "3Dconnexion");
}
}

View file

@ -175,13 +175,12 @@ public slots:
// connnects to the userinputmapper
class ConnexionData : public QObject {
class ConnexionData : public QObject, public controller::InputDevice {
Q_OBJECT
public:
static ConnexionData& getInstance();
ConnexionData();
enum PositionChannel {
POSITION_AXIS_X_POS = 1,
POSITION_AXIS_X_NEG = 2,
@ -209,19 +208,12 @@ public:
float getButton(int channel) const;
float getAxis(int channel) const;
UserInputMapper::Input makeInput(ConnexionData::PositionChannel axis);
UserInputMapper::Input makeInput(ConnexionData::ButtonChannel button);
void registerToUserInputMapper(UserInputMapper& mapper);
void assignDefaultInputMapping(UserInputMapper& mapper);
void update();
void focusOutEvent();
int getDeviceID() { return _deviceID; }
void setDeviceID(int deviceID) { _deviceID = deviceID; }
QString _name;
controller::Input makeInput(ConnexionData::PositionChannel axis);
controller::Input makeInput(ConnexionData::ButtonChannel button);
virtual void buildDeviceProxy(controller::DeviceProxy::Pointer proxy) override;
virtual QString getDefaultMappingConfig() override;
virtual void update(float deltaTime, bool jointsCaptured) override;
virtual void focusOutEvent() override;
glm::vec3 cc_position;
glm::vec3 cc_rotation;
@ -229,12 +221,6 @@ public:
void setButton(int lastButtonState);
void handleAxisEvent();
protected:
int _deviceID = 0;
ButtonPressedMap _buttonPressedMap;
AxisStateMap _axisStateMap;
};
#endif // defined(hifi_3DConnexionClient_h)

View file

@ -167,31 +167,6 @@ void ControllerScriptingInterface::releaseInputController(controller::InputContr
_inputControllers.erase(input->getKey());
}
void ControllerScriptingInterface::update() {
static float last = secTimestampNow();
float now = secTimestampNow();
float delta = now - last;
last = now;
DependencyManager::get<UserInputMapper>()->update(delta);
bool jointsCaptured = false;
for (auto inputPlugin : PluginManager::getInstance()->getInputPlugins()) {
if (inputPlugin->isActive()) {
inputPlugin->pluginUpdate(delta, jointsCaptured);
if (inputPlugin->isJointController()) {
jointsCaptured = true;
}
}
}
for (auto entry : _inputControllers) {
entry.second->update();
}
controller::ScriptingInterface::update();
}
InputController::InputController(int deviceTrackerId, int subTrackerId, QObject* parent) :
_deviceTrackerId(deviceTrackerId),
_subTrackerId(subTrackerId),

View file

@ -85,8 +85,6 @@ public:
bool isKeyCaptured(const KeyEvent& event) const;
bool isJoystickCaptured(int joystickIndex) const;
virtual void update() override;
public slots:
virtual void captureKeyEvents(const KeyEvent& event);

View file

@ -521,7 +521,7 @@ void ApplicationCompositor::renderControllerPointers(gpu::Batch& batch) {
float yAngle = 0.5f - ((atan2f(direction.z, direction.y) + (float)PI_OVER_TWO));
// Get the pixel range over which the xAngle and yAngle are scaled
float cursorRange = canvasSize.x * InputDevice::getCursorPixelRangeMult();
float cursorRange = canvasSize.x * controller::InputDevice::getCursorPixelRangeMult();
mouseX = (canvasSize.x / 2.0f + cursorRange * xAngle);
mouseY = (canvasSize.y / 2.0f + cursorRange * yAngle);

View file

@ -198,7 +198,7 @@ void PreferencesDialog::loadPreferences() {
ui.oculusUIAngularSizeSpin->setValue(qApp->getApplicationCompositor().getHmdUIAngularSize());
#endif
ui.sixenseReticleMoveSpeedSpin->setValue(InputDevice::getReticleMoveSpeed());
ui.sixenseReticleMoveSpeedSpin->setValue(controller::InputDevice::getReticleMoveSpeed());
// LOD items
auto lodManager = DependencyManager::get<LODManager>();
@ -273,7 +273,7 @@ void PreferencesDialog::savePreferences() {
qApp->getApplicationCompositor().setHmdUIAngularSize(ui.oculusUIAngularSizeSpin->value());
InputDevice::setReticleMoveSpeed(ui.sixenseReticleMoveSpeedSpin->value());
controller::InputDevice::setReticleMoveSpeed(ui.sixenseReticleMoveSpeedSpin->value());
auto audio = DependencyManager::get<AudioClient>();
MixedProcessedAudioStream& stream = audio->getReceivedAudioStream();

View file

@ -0,0 +1,71 @@
//
// Created by Bradley Austin Davis on 2015/10/19
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "Actions.h"
#include "UserInputMapper.h"
namespace controller {
// Device functions
void ActionsDevice::buildDeviceProxy(DeviceProxy::Pointer proxy) {
proxy->_name = _name;
proxy->getButton = [this](const Input& input, int timestamp) -> bool { return false; };
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, LONGITUDINAL_BACKWARD, ChannelType::AXIS), "LONGITUDINAL_BACKWARD"),
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, LONGITUDINAL_FORWARD, ChannelType::AXIS), "LONGITUDINAL_FORWARD"),
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, LATERAL_LEFT, ChannelType::AXIS), "LATERAL_LEFT"),
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, LATERAL_RIGHT, ChannelType::AXIS), "LATERAL_RIGHT"),
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, VERTICAL_DOWN, ChannelType::AXIS), "VERTICAL_DOWN"),
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, VERTICAL_UP, ChannelType::AXIS), "VERTICAL_UP"),
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, YAW_LEFT, ChannelType::AXIS), "YAW_LEFT"),
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, YAW_RIGHT, ChannelType::AXIS), "YAW_RIGHT"),
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, PITCH_DOWN, ChannelType::AXIS), "PITCH_DOWN"),
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, PITCH_UP, ChannelType::AXIS), "PITCH_UP"),
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, BOOM_IN, ChannelType::AXIS), "BOOM_IN"),
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, BOOM_OUT, ChannelType::AXIS), "BOOM_OUT"),
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, LEFT_HAND, ChannelType::POSE), "LEFT_HAND"),
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, RIGHT_HAND, ChannelType::POSE), "RIGHT_HAND"),
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, LEFT_HAND_CLICK, ChannelType::BUTTON), "LEFT_HAND_CLICK"),
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, RIGHT_HAND_CLICK, ChannelType::BUTTON), "RIGHT_HAND_CLICK"),
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, SHIFT, ChannelType::BUTTON), "SHIFT"),
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, ACTION1, ChannelType::BUTTON), "ACTION1"),
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, ACTION2, ChannelType::BUTTON), "ACTION2"),
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, CONTEXT_MENU, ChannelType::BUTTON), "CONTEXT_MENU"),
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, TOGGLE_MUTE, ChannelType::AXIS), "TOGGLE_MUTE"),
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, TRANSLATE_X, ChannelType::AXIS), "TranslateX"),
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, TRANSLATE_Y, ChannelType::AXIS), "TranslateY"),
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, TRANSLATE_Z, ChannelType::AXIS), "TranslateZ"),
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, ROLL, ChannelType::AXIS), "Roll"),
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, PITCH, ChannelType::AXIS), "Pitch"),
Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, YAW, ChannelType::AXIS), "Yaw")
};
return availableInputs;
};
}
QString ActionsDevice::getDefaultMappingConfig() {
return QString();
}
void ActionsDevice::update(float deltaTime, bool jointsCaptured) {
}
void ActionsDevice::focusOutEvent() {
}
ActionsDevice::ActionsDevice() : InputDevice("Actions") {
_deviceID = UserInputMapper::ACTIONS_DEVICE;
}
ActionsDevice::~ActionsDevice() {}
}

View file

@ -0,0 +1,100 @@
//
// Created by Bradley Austin Davis on 2015/10/19
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#pragma once
#ifndef hifi_controller_Actions_h
#define hifi_controller_Actions_h
#include <QtCore/QObject>
#include <QtCore/QVector>
#include "InputDevice.h"
namespace controller {
// Actions are the output channels of the Mapper, that's what the InputChannel map to
// For now the Actions are hardcoded, this is bad, but we will fix that in the near future
enum Action {
TRANSLATE_X = 0,
TRANSLATE_Y,
TRANSLATE_Z,
ROTATE_X, PITCH = ROTATE_X,
ROTATE_Y, YAW = ROTATE_Y,
ROTATE_Z, ROLL = ROTATE_Z,
TRANSLATE_CAMERA_Z,
NUM_COMBINED_AXES,
LEFT_HAND = NUM_COMBINED_AXES,
RIGHT_HAND,
LEFT_HAND_CLICK,
RIGHT_HAND_CLICK,
ACTION1,
ACTION2,
CONTEXT_MENU,
TOGGLE_MUTE,
SHIFT,
// Biseced aliases for TRANSLATE_Z
LONGITUDINAL_BACKWARD,
LONGITUDINAL_FORWARD,
// Biseced aliases for TRANSLATE_X
LATERAL_LEFT,
LATERAL_RIGHT,
// Biseced aliases for TRANSLATE_Y
VERTICAL_DOWN,
VERTICAL_UP,
// Biseced aliases for ROTATE_Y
YAW_LEFT,
YAW_RIGHT,
// Biseced aliases for ROTATE_X
PITCH_DOWN,
PITCH_UP,
// Biseced aliases for TRANSLATE_CAMERA_Z
BOOM_IN,
BOOM_OUT,
NUM_ACTIONS,
};
class ActionsDevice : public QObject, public InputDevice {
Q_OBJECT
Q_PROPERTY(QString name READ getName)
public:
const QString& getName() const { return _name; }
// Device functions
virtual void buildDeviceProxy(DeviceProxy::Pointer proxy) override;
virtual QString getDefaultMappingConfig() override;
virtual void update(float deltaTime, bool jointsCaptured) override;
virtual void focusOutEvent() override;
ActionsDevice();
virtual ~ActionsDevice();
};
}
#include "StandardControls.h"
#endif // hifi_StandardController_h

View file

@ -20,7 +20,7 @@ namespace controller {
return getAxis(input, timestamp);
case ChannelType::POSE:
return getPose(input, timestamp)._valid ? 1.0f : 0.0f;
return getPose(input, timestamp).valid ? 1.0f : 0.0f;
default:
return NAN;

View file

@ -35,23 +35,15 @@ namespace controller {
class DeviceProxy {
public:
DeviceProxy(QString name) : _baseName(name), _name(name) {}
const QString& getBaseName() const { return _baseName; }
const QString& getName() const { return _name; }
ButtonGetter getButton = [] (const Input& input, int timestamp) -> bool { return false; };
AxisGetter getAxis = [] (const Input& input, int timestamp) -> float { return 0.0f; };
PoseGetter getPose = [](const Input& input, int timestamp) -> Pose { return Pose(); };
AvailableInputGetter getAvailabeInputs = []() -> Input::NamedVector { return Input::NamedVector(); };
ResetBindings resetDeviceBindings = [] () -> bool { return true; };
float getValue(const Input& input, int timestamp = 0) const;
using Pointer = std::shared_ptr<DeviceProxy>;
QString _baseName;
QString _name;
using Pointer = std::shared_ptr<DeviceProxy>;
const QString& getName() const { return _name; }
ButtonGetter getButton = [] (const Input& input, int timestamp) -> bool { return false; };
AxisGetter getAxis = [] (const Input& input, int timestamp) -> float { return 0.0f; };
PoseGetter getPose = [](const Input& input, int timestamp) -> Pose { return Pose(); };
AvailableInputGetter getAvailabeInputs = []() -> Input::NamedVector const { return Input::NamedVector(); };
float getValue(const Input& input, int timestamp = 0) const;
QString _name;
};
}
#endif

View file

@ -14,7 +14,9 @@
#include <memory>
#include <functional>
#include "UserInputMapper.h"
#include <QtCore/QObject>
#include "Input.h"
class QScriptValue;
@ -24,7 +26,7 @@ namespace controller {
* i.e. Hydra.Button0, Standard.X, Action.Yaw
*/
class Endpoint : public QObject {
Q_OBJECT;
Q_OBJECT;
public:
using Pointer = std::shared_ptr<Endpoint>;
using List = std::list<Pointer>;
@ -32,18 +34,18 @@ namespace controller {
using ReadLambda = std::function<float()>;
using WriteLambda = std::function<void(float)>;
Endpoint(const UserInputMapper::Input& input) : _input(input) {}
Endpoint(const Input& input) : _input(input) {}
virtual float value() = 0;
virtual void apply(float newValue, float oldValue, const Pointer& source) = 0;
const UserInputMapper::Input& getInput() { return _input; }
const Input& getInput() { return _input; }
protected:
UserInputMapper::Input _input;
Input _input;
};
class LambdaEndpoint : public Endpoint {
public:
LambdaEndpoint(ReadLambda readLambda, WriteLambda writeLambda = [](float) {})
: Endpoint(UserInputMapper::Input::INVALID_INPUT), _readLambda(readLambda), _writeLambda(writeLambda) { }
: Endpoint(Input::INVALID_INPUT), _readLambda(readLambda), _writeLambda(writeLambda) { }
virtual float value() override { return _readLambda(); }
virtual void apply(float newValue, float oldValue, const Pointer& source) override { _writeLambda(newValue); }

View file

@ -27,26 +27,21 @@ enum class ChannelType {
// to the Action channels
struct Input {
union {
uint32_t id{ 0 }; // by default Input is 0 meaning invalid
struct {
uint16_t _device; // Up to 64K possible devices
uint16_t _channel : 13; // 2^13 possible channel per Device
uint16_t _type : 2; // 2 bits to store the Type directly in the ID
uint16_t _padding : 1; // 2 bits to store the Type directly in the ID
uint16_t device; // Up to 64K possible devices
uint16_t channel : 13 ; // 2^13 possible channel per Device
uint16_t type : 2; // 2 bits to store the Type directly in the ID
uint16_t padding : 1; // 2 bits to store the Type directly in the ID
};
uint32_t _id = 0; // by default Input is 0 meaning invalid
};
bool isValid() const { return (_id != 0); }
bool isValid() const { return (id != INVALID_INPUT.id); }
uint16_t getDevice() const { return _device; }
uint16_t getChannel() const { return _channel; }
uint32_t getID() const { return _id; }
ChannelType getType() const { return (ChannelType) _type; }
void setDevice(uint16_t device) { _device = device; }
void setChannel(uint16_t channel) { _channel = channel; }
void setType(uint16_t type) { _type = type; }
void setID(uint32_t ID) { _id = ID; }
uint16_t getDevice() const { return device; }
uint16_t getChannel() const { return channel; }
uint32_t getID() const { return id; }
ChannelType getType() const { return (ChannelType) type; }
bool isButton() const { return getType() == ChannelType::BUTTON; }
bool isAxis() const { return getType() == ChannelType::AXIS; }
@ -54,13 +49,13 @@ struct Input {
// WORKAROUND: the explicit initializer here avoids a bug in GCC-4.8.2 (but not found in 4.9.2)
// where the default initializer (a C++-11ism) for the union data above is not applied.
explicit Input() : _id(0) {}
explicit Input(uint32_t id) : _id(id) {}
explicit Input(uint16_t device, uint16_t channel, ChannelType type) : _device(device), _channel(channel), _type(uint16_t(type)), _padding(0) {}
Input(const Input& src) : _id(src._id) {}
Input& operator = (const Input& src) { _id = src._id; return (*this); }
bool operator ==(const Input& right) const { return _id == right._id; }
bool operator < (const Input& src) const { return _id < src._id; }
explicit Input() {}
explicit Input(uint32_t id) : id(id) {}
explicit Input(uint16_t device, uint16_t channel, ChannelType type) : device(device), channel(channel), type(uint16_t(type)), padding(0) {}
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& src) const { return id < src.id; }
static const Input INVALID_INPUT;
static const uint16_t INVALID_DEVICE;

View file

@ -10,75 +10,78 @@
//
#include "InputDevice.h"
bool InputDevice::_lowVelocityFilter = false;
#include "Input.h"
const float DEFAULT_HAND_RETICLE_MOVE_SPEED = 37.5f;
float InputDevice::_reticleMoveSpeed = DEFAULT_HAND_RETICLE_MOVE_SPEED;
namespace controller {
//Constants for getCursorPixelRangeMultiplier()
const float MIN_PIXEL_RANGE_MULT = 0.4f;
const float MAX_PIXEL_RANGE_MULT = 2.0f;
const float RANGE_MULT = (MAX_PIXEL_RANGE_MULT - MIN_PIXEL_RANGE_MULT) * 0.01f;
bool InputDevice::_lowVelocityFilter = false;
//Returns a multiplier to be applied to the cursor range for the controllers
float InputDevice::getCursorPixelRangeMult() {
//scales (0,100) to (MINIMUM_PIXEL_RANGE_MULT, MAXIMUM_PIXEL_RANGE_MULT)
return InputDevice::_reticleMoveSpeed * RANGE_MULT + MIN_PIXEL_RANGE_MULT;
}
const float DEFAULT_HAND_RETICLE_MOVE_SPEED = 37.5f;
float InputDevice::_reticleMoveSpeed = DEFAULT_HAND_RETICLE_MOVE_SPEED;
float InputDevice::getButton(int channel) const {
if (!_buttonPressedMap.empty()) {
if (_buttonPressedMap.find(channel) != _buttonPressedMap.end()) {
return 1.0f;
//Constants for getCursorPixelRangeMultiplier()
const float MIN_PIXEL_RANGE_MULT = 0.4f;
const float MAX_PIXEL_RANGE_MULT = 2.0f;
const float RANGE_MULT = (MAX_PIXEL_RANGE_MULT - MIN_PIXEL_RANGE_MULT) * 0.01f;
//Returns a multiplier to be applied to the cursor range for the controllers
float InputDevice::getCursorPixelRangeMult() {
//scales (0,100) to (MINIMUM_PIXEL_RANGE_MULT, MAXIMUM_PIXEL_RANGE_MULT)
return InputDevice::_reticleMoveSpeed * RANGE_MULT + MIN_PIXEL_RANGE_MULT;
}
float InputDevice::getButton(int channel) const {
if (!_buttonPressedMap.empty()) {
if (_buttonPressedMap.find(channel) != _buttonPressedMap.end()) {
return 1.0f;
} else {
return 0.0f;
}
}
else {
return 0.0f;
}
float InputDevice::getAxis(int channel) const {
auto axis = _axisStateMap.find(channel);
if (axis != _axisStateMap.end()) {
return (*axis).second;
} else {
return 0.0f;
}
}
return 0.0f;
}
float InputDevice::getAxis(int channel) const {
auto axis = _axisStateMap.find(channel);
if (axis != _axisStateMap.end()) {
return (*axis).second;
Pose InputDevice::getPose(int channel) const {
auto pose = _poseStateMap.find(channel);
if (pose != _poseStateMap.end()) {
return (*pose).second;
} else {
return Pose();
}
}
else {
return 0.0f;
Input InputDevice::makeInput(controller::StandardButtonChannel button) {
return Input(_deviceID, button, ChannelType::BUTTON);
}
}
UserInputMapper::PoseValue InputDevice::getPose(int channel) const {
auto pose = _poseStateMap.find(channel);
if (pose != _poseStateMap.end()) {
return (*pose).second;
Input InputDevice::makeInput(controller::StandardAxisChannel axis) {
return Input(_deviceID, axis, ChannelType::AXIS);
}
else {
return UserInputMapper::PoseValue();
Input InputDevice::makeInput(controller::StandardPoseChannel pose) {
return Input(_deviceID, pose, ChannelType::POSE);
}
}
UserInputMapper::Input InputDevice::makeInput(controller::StandardButtonChannel button) {
return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON);
}
Input::NamedPair InputDevice::makePair(controller::StandardButtonChannel button, const QString& name) {
return Input::NamedPair(makeInput(button), name);
}
UserInputMapper::Input InputDevice::makeInput(controller::StandardAxisChannel axis) {
return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS);
}
Input::NamedPair InputDevice::makePair(controller::StandardAxisChannel axis, const QString& name) {
return Input::NamedPair(makeInput(axis), name);
}
UserInputMapper::Input InputDevice::makeInput(controller::StandardPoseChannel pose) {
return UserInputMapper::Input(_deviceID, pose, UserInputMapper::ChannelType::POSE);
}
Input::NamedPair InputDevice::makePair(controller::StandardPoseChannel pose, const QString& name) {
return Input::NamedPair(makeInput(pose), name);
}
UserInputMapper::InputPair InputDevice::makePair(controller::StandardButtonChannel button, const QString& name) {
return UserInputMapper::InputPair(makeInput(button), name);
}
UserInputMapper::InputPair InputDevice::makePair(controller::StandardAxisChannel axis, const QString& name) {
return UserInputMapper::InputPair(makeInput(axis), name);
}
UserInputMapper::InputPair InputDevice::makePair(controller::StandardPoseChannel pose, const QString& name) {
return UserInputMapper::InputPair(makeInput(pose), name);
}

View file

@ -10,13 +10,23 @@
//
#pragma once
#include "UserInputMapper.h"
#include <memory>
#include <map>
#include <unordered_set>
#include <QtCore/QString>
#include "Pose.h"
#include "Input.h"
#include "StandardControls.h"
#include "DeviceProxy.h"
// Event types for each controller
const unsigned int CONTROLLER_0_EVENT = 1500U;
const unsigned int CONTROLLER_1_EVENT = 1501U;
namespace controller {
// NOTE: If something inherits from both InputDevice and InputPlugin, InputPlugin must go first.
// e.g. class Example : public InputPlugin, public InputDevice
// instead of class Example : public InputDevice, public InputPlugin
@ -24,17 +34,19 @@ class InputDevice {
public:
InputDevice(const QString& name) : _name(name) {}
using Pointer = std::shared_ptr<InputDevice>;
typedef std::unordered_set<int> ButtonPressedMap;
typedef std::map<int, float> AxisStateMap;
typedef std::map<int, UserInputMapper::PoseValue> PoseStateMap;
typedef std::map<int, Pose> PoseStateMap;
// Get current state for each channel
float getButton(int channel) const;
float getAxis(int channel) const;
UserInputMapper::PoseValue getPose(int channel) const;
Pose getPose(int channel) const;
virtual void registerToUserInputMapper(UserInputMapper& mapper) = 0;
virtual void assignDefaultInputMapping(UserInputMapper& mapper) = 0;
virtual void buildDeviceProxy(DeviceProxy::Pointer proxy) = 0;
virtual QString getDefaultMappingConfig() = 0;
// Update call MUST be called once per simulation loop
// It takes care of updating the action states and deltas
@ -43,6 +55,7 @@ public:
virtual void focusOutEvent() = 0;
int getDeviceID() { return _deviceID; }
void setDeviceID(int deviceID) { _deviceID = deviceID; }
static float getCursorPixelRangeMult();
static float getReticleMoveSpeed() { return _reticleMoveSpeed; }
@ -50,18 +63,18 @@ public:
static bool getLowVelocityFilter() { return _lowVelocityFilter; };
UserInputMapper::Input makeInput(controller::StandardButtonChannel button);
UserInputMapper::Input makeInput(controller::StandardAxisChannel axis);
UserInputMapper::Input makeInput(controller::StandardPoseChannel pose);
UserInputMapper::InputPair makePair(controller::StandardButtonChannel button, const QString& name);
UserInputMapper::InputPair makePair(controller::StandardAxisChannel button, const QString& name);
UserInputMapper::InputPair makePair(controller::StandardPoseChannel button, const QString& name);
public slots:
Input makeInput(StandardButtonChannel button);
Input makeInput(StandardAxisChannel axis);
Input makeInput(StandardPoseChannel pose);
Input::NamedPair makePair(StandardButtonChannel button, const QString& name);
Input::NamedPair makePair(StandardAxisChannel button, const QString& name);
Input::NamedPair makePair(StandardPoseChannel button, const QString& name);
public slots:
static void setLowVelocityFilter(bool newLowVelocityFilter) { _lowVelocityFilter = newLowVelocityFilter; };
protected:
int _deviceID = 0;
friend class UserInputMapper;
uint16_t _deviceID{ Input::INVALID_DEVICE };
QString _name;
@ -73,4 +86,6 @@ protected:
private:
static float _reticleMoveSpeed;
};
};
}

View file

@ -6,11 +6,3 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "Mapping.h"
namespace controller {
Mapping::Mapping(const QString& name ) : _name(name) {
}
}

View file

@ -27,13 +27,11 @@ namespace controller {
using Map = std::map<Endpoint::Pointer, Route::List>;
using Pointer = std::shared_ptr<Mapping>;
Mapping(const QString& name);
Mapping(const QString& name) : name(name) {}
Map _channelMappings;
Map channelMappings;
QString _name;
protected:
QString name;
};
}

View file

@ -12,17 +12,17 @@ namespace controller {
Pose::Pose(const vec3& translation, const quat& rotation,
const vec3& velocity, const quat& angularVelocity) :
_translation(translation), _rotation(rotation), _velocity(velocity), _angularVelocity(angularVelocity) { }
translation(translation), rotation(rotation), velocity(velocity), angularVelocity(angularVelocity) { }
bool Pose::operator==(const Pose& right) const {
// invalid poses return false for comparison, even against identical invalid poses, like NaN
if (_valid || !right._valid) {
if (!valid || !right.valid) {
return false;
}
// FIXME add margin of error? Or add an additional withinEpsilon function?
return _translation == right.getTranslation() && _rotation == right.getRotation() &&
_velocity == right.getVelocity() && _angularVelocity == right.getAngularVelocity();
return translation == right.getTranslation() && rotation == right.getRotation() &&
velocity == right.getVelocity() && angularVelocity == right.getAngularVelocity();
}

View file

@ -17,11 +17,11 @@ namespace controller {
struct Pose {
public:
vec3 _translation;
quat _rotation;
vec3 _velocity;
quat _angularVelocity;
bool _valid{ false };
vec3 translation;
quat rotation;
vec3 velocity;
quat angularVelocity;
bool valid{ false };
Pose() {}
Pose(const vec3& translation, const quat& rotation,
@ -30,11 +30,11 @@ namespace controller {
Pose(const Pose&) = default;
Pose& operator = (const Pose&) = default;
bool operator ==(const Pose& right) const;
bool isValid() const { return _valid; }
vec3 getTranslation() const { return _translation; }
quat getRotation() const { return _rotation; }
vec3 getVelocity() const { return _velocity; }
quat getAngularVelocity() const { return _angularVelocity; }
bool isValid() const { return valid; }
vec3 getTranslation() const { return translation; }
quat getRotation() const { return rotation; }
vec3 getVelocity() const { return velocity; }
quat getAngularVelocity() const { return angularVelocity; }
};

View file

@ -6,5 +6,4 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "Route.h"

View file

@ -16,20 +16,17 @@
class QJsonObject;
namespace controller {
/*
* encapsulates a source, destination and filters to apply
*/
class Route {
public:
Endpoint::Pointer _source;
Endpoint::Pointer _destination;
Filter::List _filters;
Endpoint::Pointer source;
Endpoint::Pointer destination;
Filter::List filters;
using Pointer = std::shared_ptr<Route>;
using List = std::list<Pointer>;
void parse(const QJsonObject);
};
}

View file

@ -12,8 +12,6 @@
#include <QtCore/QRegularExpression>
#include <QJsonDocument>
#include <QJsonObject>
#include <QEventLoop>
#include <QThread>
@ -26,475 +24,146 @@
#include "Logging.h"
#include "InputDevice.h"
static QRegularExpression SANITIZE_NAME_EXPRESSION{ "[\\(\\)\\.\\s]" };
static QVariantMap createDeviceMap(const controller::DeviceProxy::Pointer device) {
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
QVariantMap deviceMap;
for (const auto& inputMapping : device->getAvailabeInputs()) {
const auto& input = inputMapping.first;
const auto inputName = QString(inputMapping.second).remove(SANITIZE_NAME_EXPRESSION);
qCDebug(controllers) << "\tInput " << input.getChannel() << (int)input.getType()
<< QString::number(input.getID(), 16) << ": " << inputName;
deviceMap.insert(inputName, input.getID());
}
return deviceMap;
}
// FIXME this throws a hissy fit on MSVC if I put it in the main controller namespace block
controller::ScriptingInterface::ScriptingInterface() {
auto userInputMapper = DependencyManager::get<UserInputMapper>();
// FIXME make this thread safe
connect(userInputMapper.data(), &UserInputMapper::hardwareChanged, [=] {
updateMaps();
});
qCDebug(controllers) << "Setting up standard controller abstraction";
_standard = createDeviceMap(userInputMapper->getStandardDevice());
// FIXME allow custom user actions?
auto actionNames = userInputMapper->getActionNames();
int actionNumber = 0;
qCDebug(controllers) << "Setting up standard actions";
for (const auto& namedInput : userInputMapper->getActionInputs()) {
const QString& actionName = namedInput.second;
const Input& actionInput = namedInput.first;
qCDebug(controllers) << "\tAction: " << actionName << " " << actionInput.getChannel();
// Expose the IDs to JS
QString cleanActionName = QString(actionName).remove(SANITIZE_NAME_EXPRESSION);
_actions.insert(cleanActionName, actionInput.getID());
}
updateMaps();
}
namespace controller {
class VirtualEndpoint : public Endpoint {
public:
VirtualEndpoint(const UserInputMapper::Input& id = UserInputMapper::Input::INVALID_INPUT)
: Endpoint(id) {
}
virtual float value() override { return _currentValue; }
virtual void apply(float newValue, float oldValue, const Pointer& source) override { _currentValue = newValue; }
private:
float _currentValue{ 0.0f };
};
class JSEndpoint : public Endpoint {
public:
JSEndpoint(const QJSValue& callable)
: Endpoint(UserInputMapper::Input::INVALID_INPUT), _callable(callable) {}
virtual float value() {
float result = (float)_callable.call().toNumber();;
return result;
}
virtual void apply(float newValue, float oldValue, const Pointer& source) {
_callable.call(QJSValueList({ QJSValue(newValue) }));
}
private:
QJSValue _callable;
};
float ScriptEndpoint::value() {
updateValue();
return _lastValue;
}
void ScriptEndpoint::updateValue() {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "updateValue", Qt::QueuedConnection);
return;
}
_lastValue = (float)_callable.call().toNumber();
}
void ScriptEndpoint::apply(float newValue, float oldValue, const Pointer& source) {
internalApply(newValue, oldValue, source->getInput().getID());
}
void ScriptEndpoint::internalApply(float newValue, float oldValue, int sourceID) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "internalApply", Qt::QueuedConnection,
Q_ARG(float, newValue),
Q_ARG(float, oldValue),
Q_ARG(int, sourceID));
return;
}
_callable.call(QScriptValue(),
QScriptValueList({ QScriptValue(newValue), QScriptValue(oldValue), QScriptValue(sourceID) }));
}
class CompositeEndpoint : public Endpoint, Endpoint::Pair {
public:
CompositeEndpoint(Endpoint::Pointer first, Endpoint::Pointer second)
: Endpoint(UserInputMapper::Input(UserInputMapper::Input::INVALID_INPUT)), Pair(first, second) { }
virtual float value() {
float result = first->value() * -1.0f + second->value();
return result;
}
virtual void apply(float newValue, float oldValue, const Pointer& source) {
// Composites are read only
}
private:
Endpoint::Pointer _first;
Endpoint::Pointer _second;
};
class ActionEndpoint : public Endpoint {
public:
ActionEndpoint(const UserInputMapper::Input& id = UserInputMapper::Input::INVALID_INPUT)
: Endpoint(id) {
}
virtual float value() override { return _currentValue; }
virtual void apply(float newValue, float oldValue, const Pointer& source) override {
_currentValue += newValue;
if (!(_input == UserInputMapper::Input::INVALID_INPUT)) {
auto userInputMapper = DependencyManager::get<UserInputMapper>();
userInputMapper->deltaActionState(UserInputMapper::Action(_input.getChannel()), newValue);
}
}
private:
float _currentValue{ 0.0f };
};
QRegularExpression ScriptingInterface::SANITIZE_NAME_EXPRESSION{ "[\\(\\)\\.\\s]" };
QVariantMap createDeviceMap(const UserInputMapper::DeviceProxy* device) {
auto userInputMapper = DependencyManager::get<UserInputMapper>();
QVariantMap deviceMap;
for (const auto& inputMapping : device->getAvailabeInputs()) {
const auto& input = inputMapping.first;
const auto inputName = QString(inputMapping.second).remove(ScriptingInterface::SANITIZE_NAME_EXPRESSION);
qCDebug(controllers) << "\tInput " << input.getChannel() << (int)input.getType()
<< QString::number(input.getID(), 16) << ": " << inputName;
deviceMap.insert(inputName, input.getID());
}
return deviceMap;
}
ScriptingInterface::~ScriptingInterface() {
}
QObject* ScriptingInterface::newMapping(const QString& mappingName) {
if (_mappingsByName.count(mappingName)) {
qCWarning(controllers) << "Refusing to recreate mapping named " << mappingName;
}
qDebug() << "Creating new Mapping " << mappingName;
auto mapping = std::make_shared<Mapping>(mappingName);
_mappingsByName[mappingName] = mapping;
return new MappingBuilderProxy(*this, mapping);
auto userInputMapper = DependencyManager::get<UserInputMapper>();
return new MappingBuilderProxy(*userInputMapper, userInputMapper->newMapping(mappingName));
}
QObject* ScriptingInterface::parseMapping(const QString& json) {
QJsonObject obj;
QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson(json.toUtf8(), &error);
// check validity of the document
if (!doc.isNull()) {
if (doc.isObject()) {
obj = doc.object();
auto mapping = std::make_shared<Mapping>("default");
auto mappingBuilder = new MappingBuilderProxy(*this, mapping);
mappingBuilder->parse(obj);
_mappingsByName[mapping->_name] = mapping;
return mappingBuilder;
} else {
qDebug() << "Mapping json Document is not an object" << endl;
}
} else {
qDebug() << "Invalid JSON...\n";
qDebug() << error.errorString();
qDebug() << "JSON was:\n" << json << endl;
}
return nullptr;
}
QObject* ScriptingInterface::loadMapping(const QString& jsonUrl) {
QObject* result = nullptr;
auto request = ResourceManager::createResourceRequest(nullptr, QUrl(jsonUrl));
if (request) {
QEventLoop eventLoop;
request->setCacheEnabled(false);
connect(request, &ResourceRequest::finished, &eventLoop, &QEventLoop::quit);
request->send();
if (request->getState() != ResourceRequest::Finished) {
eventLoop.exec();
}
if (request->getResult() == ResourceRequest::Success) {
result = parseMapping(QString(request->getData()));
} else {
qCWarning(controllers) << "Failed to load mapping url <" << jsonUrl << ">" << endl;
}
request->deleteLater();
}
return result;
}
Q_INVOKABLE QObject* newMapping(const QJsonObject& json);
void ScriptingInterface::enableMapping(const QString& mappingName, bool enable) {
qCDebug(controllers) << "Attempting to enable mapping " << mappingName;
auto iterator = _mappingsByName.find(mappingName);
if (_mappingsByName.end() == iterator) {
qCWarning(controllers) << "Request to enable / disable unknown mapping " << mappingName;
return;
}
auto mapping = iterator->second;
if (enable) {
_activeMappings.push_front(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);
}
auto userInputMapper = DependencyManager::get<UserInputMapper>();
userInputMapper->enableMapping(mappingName, enable);
}
float ScriptingInterface::getValue(const int& source) const {
// return (sin(secTimestampNow()) + 1.0f) / 2.0f;
UserInputMapper::Input input(source);
auto iterator = _endpoints.find(input);
if (_endpoints.end() == iterator) {
return 0.0;
}
const auto& endpoint = iterator->second;
return getValue(endpoint);
}
float ScriptingInterface::getValue(const Endpoint::Pointer& endpoint) const {
auto valuesIterator = _overrideValues.find(endpoint);
if (_overrideValues.end() != valuesIterator) {
return valuesIterator->second;
}
return endpoint->value();
auto userInputMapper = DependencyManager::get<UserInputMapper>();
return userInputMapper->getValue(Input((uint32_t)source));
}
float ScriptingInterface::getButtonValue(StandardButtonChannel source, uint16_t device) const {
return getValue(UserInputMapper::Input(device, source, UserInputMapper::ChannelType::BUTTON).getID());
return getValue(Input(device, source, ChannelType::BUTTON).getID());
}
float ScriptingInterface::getAxisValue(StandardAxisChannel source, uint16_t device) const {
return getValue(UserInputMapper::Input(device, source, UserInputMapper::ChannelType::AXIS).getID());
return getValue(Input(device, source, ChannelType::AXIS).getID());
}
Pose ScriptingInterface::getPoseValue(StandardPoseChannel source, uint16_t device) const {
return Pose();
}
void ScriptingInterface::update() {
auto userInputMapper = DependencyManager::get<UserInputMapper>();
static auto deviceNames = userInputMapper->getDeviceNames();
//bool ScriptingInterface::isPrimaryButtonPressed() const {
// return isButtonPressed(StandardButtonChannel::A);
//}
//
//glm::vec2 ScriptingInterface::getPrimaryJoystickPosition() const {
// return getJoystickPosition(0);
//}
if (deviceNames != userInputMapper->getDeviceNames()) {
updateMaps();
deviceNames = userInputMapper->getDeviceNames();
}
//int ScriptingInterface::getNumberOfButtons() const {
// return StandardButtonChannel::NUM_STANDARD_BUTTONS;
//}
_overrideValues.clear();
EndpointSet readEndpoints;
EndpointSet writtenEndpoints;
// Now process the current values for each level of the stack
for (auto& mapping : _activeMappings) {
for (const auto& mappingEntry : mapping->_channelMappings) {
const auto& source = mappingEntry.first;
//bool ScriptingInterface::isButtonPressed(int buttonIndex) const {
// return getButtonValue((StandardButtonChannel)buttonIndex) == 0.0 ? false : true;
//}
// 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;
}
//int ScriptingInterface::getNumberOfTriggers() const {
// return StandardCounts::TRIGGERS;
//}
// Apply the value to all the routes
const auto& routes = mappingEntry.second;
//float ScriptingInterface::getTriggerValue(int triggerIndex) const {
// return getAxisValue(triggerIndex == 0 ? StandardAxisChannel::LT : StandardAxisChannel::RT);
//}
for (const auto& route : routes) {
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 (!source || !destination) {
continue;
}
//int ScriptingInterface::getNumberOfJoysticks() const {
// return StandardCounts::ANALOG_STICKS;
//}
if (writtenEndpoints.count(destination)) {
continue;
}
//glm::vec2 ScriptingInterface::getJoystickPosition(int joystickIndex) const {
// StandardAxisChannel xid = StandardAxisChannel::LX;
// StandardAxisChannel yid = StandardAxisChannel::LY;
// if (joystickIndex != 0) {
// xid = StandardAxisChannel::RX;
// yid = StandardAxisChannel::RY;
// }
// vec2 result;
// result.x = getAxisValue(xid);
// result.y = getAxisValue(yid);
// return result;
//}
// Standard controller destinations can only be can only be used once.
if (userInputMapper->getStandardDeviceID() == destination->getInput().getDevice()) {
writtenEndpoints.insert(destination);
}
//int ScriptingInterface::getNumberOfSpatialControls() const {
// return StandardCounts::POSES;
//}
// 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);
}
//glm::vec3 ScriptingInterface::getSpatialControlPosition(int controlIndex) const {
// // FIXME extract the position from the standard pose
// return vec3();
//}
// Fetch the value, may have been overriden by previous loopback routes
float value = getValue(source);
//glm::vec3 ScriptingInterface::getSpatialControlVelocity(int controlIndex) const {
// // FIXME extract the velocity from the standard pose
// return vec3();
//}
// Apply each of the filters.
for (const auto& filter : route->_filters) {
value = filter->apply(value);
}
//glm::vec3 ScriptingInterface::getSpatialControlNormal(int controlIndex) const {
// // FIXME extract the normal from the standard pose
// return vec3();
//}
//
//glm::quat ScriptingInterface::getSpatialControlRawRotation(int controlIndex) const {
// // FIXME extract the rotation from the standard pose
// return quat();
//}
if (loopback) {
_overrideValues[source] = value;
} else {
destination->apply(value, 0, source);
}
}
}
}
}
Endpoint::Pointer ScriptingInterface::endpointFor(const QJSValue& endpoint) {
if (endpoint.isNumber()) {
return endpointFor(UserInputMapper::Input(endpoint.toInt()));
}
if (endpoint.isCallable()) {
auto result = std::make_shared<JSEndpoint>(endpoint);
return result;
}
qWarning() << "Unsupported input type " << endpoint.toString();
return Endpoint::Pointer();
}
Endpoint::Pointer ScriptingInterface::endpointFor(const QScriptValue& endpoint) {
if (endpoint.isNumber()) {
return endpointFor(UserInputMapper::Input(endpoint.toInt32()));
}
if (endpoint.isFunction()) {
auto result = std::make_shared<ScriptEndpoint>(endpoint);
return result;
}
qWarning() << "Unsupported input type " << endpoint.toString();
return Endpoint::Pointer();
}
UserInputMapper::Input ScriptingInterface::inputFor(const QString& inputName) {
return DependencyManager::get<UserInputMapper>()->findDeviceInput(inputName);
}
Endpoint::Pointer ScriptingInterface::endpointFor(const UserInputMapper::Input& inputId) {
auto iterator = _endpoints.find(inputId);
if (_endpoints.end() == iterator) {
qWarning() << "Unknown input: " << QString::number(inputId.getID(), 16);
return Endpoint::Pointer();
}
return iterator->second;
}
Endpoint::Pointer ScriptingInterface::compositeEndpointFor(Endpoint::Pointer first, Endpoint::Pointer second) {
EndpointPair pair(first, second);
Endpoint::Pointer result;
auto iterator = _compositeEndpoints.find(pair);
if (_compositeEndpoints.end() == iterator) {
result = std::make_shared<CompositeEndpoint>(first, second);
_compositeEndpoints[pair] = result;
} else {
result = iterator->second;
}
return result;
}
bool ScriptingInterface::isPrimaryButtonPressed() const {
return isButtonPressed(StandardButtonChannel::A);
}
glm::vec2 ScriptingInterface::getPrimaryJoystickPosition() const {
return getJoystickPosition(0);
}
int ScriptingInterface::getNumberOfButtons() const {
return StandardButtonChannel::NUM_STANDARD_BUTTONS;
}
bool ScriptingInterface::isButtonPressed(int buttonIndex) const {
return getButtonValue((StandardButtonChannel)buttonIndex) == 0.0f ? false : true;
}
int ScriptingInterface::getNumberOfTriggers() const {
return StandardCounts::TRIGGERS;
}
float ScriptingInterface::getTriggerValue(int triggerIndex) const {
return getAxisValue(triggerIndex == 0 ? StandardAxisChannel::LT : StandardAxisChannel::RT);
}
int ScriptingInterface::getNumberOfJoysticks() const {
return StandardCounts::ANALOG_STICKS;
}
glm::vec2 ScriptingInterface::getJoystickPosition(int joystickIndex) const {
StandardAxisChannel xid = StandardAxisChannel::LX;
StandardAxisChannel yid = StandardAxisChannel::LY;
if (joystickIndex != 0) {
xid = StandardAxisChannel::RX;
yid = StandardAxisChannel::RY;
}
vec2 result;
result.x = getAxisValue(xid);
result.y = getAxisValue(yid);
return result;
}
int ScriptingInterface::getNumberOfSpatialControls() const {
return StandardCounts::POSES;
}
glm::vec3 ScriptingInterface::getSpatialControlPosition(int controlIndex) const {
// FIXME extract the position from the standard pose
return vec3();
}
glm::vec3 ScriptingInterface::getSpatialControlVelocity(int controlIndex) const {
// FIXME extract the velocity from the standard pose
return vec3();
}
glm::vec3 ScriptingInterface::getSpatialControlNormal(int controlIndex) const {
// FIXME extract the normal from the standard pose
return vec3();
}
glm::quat ScriptingInterface::getSpatialControlRawRotation(int controlIndex) const {
// FIXME extract the rotation from the standard pose
return quat();
}
void ScriptingInterface::updateMaps() {
auto userInputMapper = DependencyManager::get<UserInputMapper>();
auto devices = userInputMapper->getDevices();
QSet<QString> foundDevices;
for (const auto& deviceMapping : devices) {
auto deviceID = deviceMapping.first;
if (deviceID != userInputMapper->getStandardDeviceID()) {
auto device = deviceMapping.second.get();
auto deviceName = QString(device->getName()).remove(ScriptingInterface::SANITIZE_NAME_EXPRESSION);
qCDebug(controllers) << "Device" << deviceMapping.first << ":" << deviceName;
foundDevices.insert(device->getName());
if (_hardware.contains(deviceName)) {
continue;
}
// Expose the IDs to JS
_hardware.insert(deviceName, createDeviceMap(device));
// Create the endpoints
for (const auto& inputMapping : device->getAvailabeInputs()) {
const auto& input = inputMapping.first;
// Ignore aliases
if (_endpoints.count(input)) {
continue;
}
_endpoints[input] = std::make_shared<LambdaEndpoint>([=] {
auto deviceProxy = userInputMapper->getDeviceProxy(input);
if (!deviceProxy) {
return 0.0f;
}
return deviceProxy->getValue(input, 0);
});
}
}
}
}
QVector<UserInputMapper::Action> ScriptingInterface::getAllActions() {
QVector<Action> ScriptingInterface::getAllActions() {
return DependencyManager::get<UserInputMapper>()->getAllActions();
}
@ -502,7 +171,7 @@ namespace controller {
return DependencyManager::get<UserInputMapper>()->getDeviceName((unsigned short)device);
}
QVector<UserInputMapper::InputPair> ScriptingInterface::getAvailableInputs(unsigned int device) {
QVector<InputPair> ScriptingInterface::getAvailableInputs(unsigned int device) {
return DependencyManager::get<UserInputMapper>()->getAvailableInputs((unsigned short)device);
}
@ -515,7 +184,7 @@ namespace controller {
}
float ScriptingInterface::getActionValue(int action) {
return DependencyManager::get<UserInputMapper>()->getActionState(UserInputMapper::Action(action));
return DependencyManager::get<UserInputMapper>()->getActionState(Action(action));
}
int ScriptingInterface::findAction(QString actionName) {
@ -526,41 +195,40 @@ namespace controller {
return DependencyManager::get<UserInputMapper>()->getActionNames();
}
void ScriptingInterface::updateMaps() {
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
auto devices = userInputMapper->getDevices();
QSet<QString> foundDevices;
for (const auto& deviceMapping : devices) {
auto deviceID = deviceMapping.first;
if (deviceID != userInputMapper->getStandardDeviceID()) {
auto device = deviceMapping.second;
auto deviceName = QString(device->getName()).remove(SANITIZE_NAME_EXPRESSION);
qCDebug(controllers) << "Device" << deviceMapping.first << ":" << deviceName;
foundDevices.insert(device->getName());
if (_hardware.contains(deviceName)) {
continue;
}
// Expose the IDs to JS
_hardware.insert(deviceName, createDeviceMap(device));
}
}
}
QObject* ScriptingInterface::parseMapping(const QString& json) {
auto userInputMapper = DependencyManager::get<UserInputMapper>();
auto mapping = userInputMapper->parseMapping(json);
return new MappingBuilderProxy(*userInputMapper, mapping);
}
QObject* ScriptingInterface::loadMapping(const QString& jsonUrl) {
return nullptr;
}
} // namespace controllers
using namespace controller;
// FIXME this throws a hissy fit on MSVC if I put it in the main controller namespace block
ScriptingInterface::ScriptingInterface() {
auto userInputMapper = DependencyManager::get<UserInputMapper>();
qCDebug(controllers) << "Setting up standard controller abstraction";
auto standardDevice = userInputMapper->getStandardDevice();
// Expose the IDs to JS
_standard = createDeviceMap(standardDevice.get());
// Create the endpoints
for (const auto& inputMapping : standardDevice->getAvailabeInputs()) {
const auto& standardInput = inputMapping.first;
// Ignore aliases
if (_endpoints.count(standardInput)) {
continue;
}
_endpoints[standardInput] = std::make_shared<VirtualEndpoint>(standardInput);
}
// FIXME allow custom user actions?
auto actionNames = userInputMapper->getActionNames();
int actionNumber = 0;
qCDebug(controllers) << "Setting up standard actions";
for (const auto& actionName : actionNames) {
UserInputMapper::Input actionInput(UserInputMapper::ACTIONS_DEVICE, actionNumber++, UserInputMapper::ChannelType::AXIS);
qCDebug(controllers) << "\tAction: " << actionName << " " << QString::number(actionInput.getID(), 16);
// Expose the IDs to JS
QString cleanActionName = QString(actionName).remove(ScriptingInterface::SANITIZE_NAME_EXPRESSION);
_actions.insert(cleanActionName, actionInput.getID());
// Create the action endpoints
_endpoints[actionInput] = std::make_shared<ActionEndpoint>(actionInput);
}
updateMaps();
}

View file

@ -32,7 +32,6 @@
#include "UserInputMapper.h"
#include "StandardControls.h"
#include "Mapping.h"
namespace controller {
class InputController : public QObject {
@ -65,10 +64,10 @@ namespace controller {
public:
ScriptingInterface();
virtual ~ScriptingInterface();
virtual ~ScriptingInterface() {};
Q_INVOKABLE QVector<UserInputMapper::Action> getAllActions();
Q_INVOKABLE QVector<UserInputMapper::InputPair> getAvailableInputs(unsigned int device);
Q_INVOKABLE QVector<Action> getAllActions();
Q_INVOKABLE QVector<Input::NamedPair> getAvailableInputs(unsigned int device);
Q_INVOKABLE QString getDeviceName(unsigned int device);
Q_INVOKABLE float getActionValue(int action);
Q_INVOKABLE int findDevice(QString name);
@ -87,23 +86,23 @@ namespace controller {
Q_INVOKABLE QObject* loadMapping(const QString& jsonUrl);
Q_INVOKABLE bool isPrimaryButtonPressed() const;
Q_INVOKABLE glm::vec2 getPrimaryJoystickPosition() const;
//Q_INVOKABLE bool isPrimaryButtonPressed() const;
//Q_INVOKABLE glm::vec2 getPrimaryJoystickPosition() const;
Q_INVOKABLE int getNumberOfButtons() const;
Q_INVOKABLE bool isButtonPressed(int buttonIndex) const;
//Q_INVOKABLE int getNumberOfButtons() const;
//Q_INVOKABLE bool isButtonPressed(int buttonIndex) const;
Q_INVOKABLE int getNumberOfTriggers() const;
Q_INVOKABLE float getTriggerValue(int triggerIndex) const;
//Q_INVOKABLE int getNumberOfTriggers() const;
//Q_INVOKABLE float getTriggerValue(int triggerIndex) const;
Q_INVOKABLE int getNumberOfJoysticks() const;
Q_INVOKABLE glm::vec2 getJoystickPosition(int joystickIndex) const;
//Q_INVOKABLE int getNumberOfJoysticks() const;
//Q_INVOKABLE glm::vec2 getJoystickPosition(int joystickIndex) const;
Q_INVOKABLE int getNumberOfSpatialControls() const;
Q_INVOKABLE glm::vec3 getSpatialControlPosition(int controlIndex) const;
Q_INVOKABLE glm::vec3 getSpatialControlVelocity(int controlIndex) const;
Q_INVOKABLE glm::vec3 getSpatialControlNormal(int controlIndex) const;
Q_INVOKABLE glm::quat getSpatialControlRawRotation(int controlIndex) const;
//Q_INVOKABLE int getNumberOfSpatialControls() const;
//Q_INVOKABLE glm::vec3 getSpatialControlPosition(int controlIndex) const;
//Q_INVOKABLE glm::vec3 getSpatialControlVelocity(int controlIndex) const;
//Q_INVOKABLE glm::vec3 getSpatialControlNormal(int controlIndex) const;
//Q_INVOKABLE glm::quat getSpatialControlRawRotation(int controlIndex) const;
Q_INVOKABLE const QVariantMap& getHardware() { return _hardware; }
Q_INVOKABLE const QVariantMap& getActions() { return _actions; }
@ -114,11 +113,7 @@ namespace controller {
bool isWheelCaptured() const { return _wheelCaptured; }
bool areActionsCaptured() const { return _actionsCaptured; }
static QRegularExpression SANITIZE_NAME_EXPRESSION;
public slots:
virtual void update();
virtual void updateMaps();
virtual void captureMouseEvents() { _mouseCaptured = true; }
virtual void releaseMouseEvents() { _mouseCaptured = false; }
@ -134,61 +129,19 @@ namespace controller {
private:
friend class MappingBuilderProxy;
friend class RouteBuilderProxy;
// FIXME move to unordered set / map
using MappingMap = std::map<QString, Mapping::Pointer>;
using MappingStack = std::list<Mapping::Pointer>;
using InputToEndpointMap = std::map<UserInputMapper::Input, Endpoint::Pointer>;
using EndpointSet = std::unordered_set<Endpoint::Pointer>;
using ValueMap = std::map<Endpoint::Pointer, float>;
using EndpointPair = std::pair<Endpoint::Pointer, Endpoint::Pointer>;
using EndpointPairMap = std::map<EndpointPair, Endpoint::Pointer>;
void update(Mapping::Pointer& mapping, EndpointSet& consumed);
float getValue(const Endpoint::Pointer& endpoint) const;
Endpoint::Pointer endpointFor(const QJSValue& endpoint);
Endpoint::Pointer endpointFor(const QScriptValue& endpoint);
Endpoint::Pointer endpointFor(const UserInputMapper::Input& endpoint);
Endpoint::Pointer compositeEndpointFor(Endpoint::Pointer first, Endpoint::Pointer second);
UserInputMapper::Input inputFor(const QString& inputName);
// Update the exposed variant maps reporting active hardware
void updateMaps();
QVariantMap _hardware;
QVariantMap _actions;
QVariantMap _standard;
InputToEndpointMap _endpoints;
EndpointPairMap _compositeEndpoints;
ValueMap _overrideValues;
MappingMap _mappingsByName;
MappingStack _activeMappings;
bool _mouseCaptured{ false };
bool _touchCaptured{ false };
bool _wheelCaptured{ false };
bool _actionsCaptured{ false };
};
class ScriptEndpoint : public Endpoint {
Q_OBJECT;
public:
ScriptEndpoint(const QScriptValue& callable)
: Endpoint(UserInputMapper::Input::INVALID_INPUT), _callable(callable) {
}
virtual float value();
virtual void apply(float newValue, float oldValue, const Pointer& source);
protected:
Q_INVOKABLE void updateValue();
Q_INVOKABLE virtual void internalApply(float newValue, float oldValue, int sourceID);
private:
QScriptValue _callable;
float _lastValue = 0.0f;
};
}

View file

@ -9,12 +9,21 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <limits>
#include <glm/glm.hpp>
#include "StandardController.h"
#include <PathUtils.h>
#include "DeviceProxy.h"
#include "UserInputMapper.h"
namespace controller {
const float CONTROLLER_THRESHOLD = 0.3f;
StandardController::StandardController() : InputDevice("Standard") {
_deviceID = UserInputMapper::STANDARD_DEVICE;
}
StandardController::~StandardController() {
}
@ -26,143 +35,111 @@ void StandardController::focusOutEvent() {
_buttonPressedMap.clear();
};
void StandardController::registerToUserInputMapper(UserInputMapper& mapper) {
// Grab the current free device ID
_deviceID = mapper.getStandardDeviceID();
auto proxy = std::make_shared<UserInputMapper::DeviceProxy>(_name);
proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); };
proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); };
proxy->getAvailabeInputs = [this] () -> QVector<UserInputMapper::InputPair> {
QVector<UserInputMapper::InputPair> availableInputs;
void StandardController::buildDeviceProxy(DeviceProxy::Pointer proxy) {
proxy->_name = _name;
proxy->getButton = [this] (const Input& input, int timestamp) -> bool { return getButton(input.getChannel()); };
proxy->getAxis = [this] (const Input& input, int timestamp) -> float { return getAxis(input.getChannel()); };
proxy->getAvailabeInputs = [this] () -> QVector<Input::NamedPair> {
QVector<Input::NamedPair> availableInputs;
// Buttons
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::A), "A"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::B), "B"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::X), "X"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::Y), "Y"));
availableInputs.append(Input::NamedPair(makeInput(controller::A), "A"));
availableInputs.append(Input::NamedPair(makeInput(controller::B), "B"));
availableInputs.append(Input::NamedPair(makeInput(controller::X), "X"));
availableInputs.append(Input::NamedPair(makeInput(controller::Y), "Y"));
// DPad
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DU), "DU"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DD), "DD"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DL), "DL"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DR), "DR"));
availableInputs.append(Input::NamedPair(makeInput(controller::DU), "DU"));
availableInputs.append(Input::NamedPair(makeInput(controller::DD), "DD"));
availableInputs.append(Input::NamedPair(makeInput(controller::DL), "DL"));
availableInputs.append(Input::NamedPair(makeInput(controller::DR), "DR"));
// Bumpers
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LB), "LB"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RB), "RB"));
availableInputs.append(Input::NamedPair(makeInput(controller::LB), "LB"));
availableInputs.append(Input::NamedPair(makeInput(controller::RB), "RB"));
// Stick press
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LS), "LS"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RS), "RS"));
availableInputs.append(Input::NamedPair(makeInput(controller::LS), "LS"));
availableInputs.append(Input::NamedPair(makeInput(controller::RS), "RS"));
// Center buttons
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::START), "Start"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::BACK), "Back"));
availableInputs.append(Input::NamedPair(makeInput(controller::START), "Start"));
availableInputs.append(Input::NamedPair(makeInput(controller::BACK), "Back"));
// Analog sticks
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LY), "LY"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LX), "LX"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RY), "RY"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RX), "RX"));
availableInputs.append(Input::NamedPair(makeInput(controller::LY), "LY"));
availableInputs.append(Input::NamedPair(makeInput(controller::LX), "LX"));
availableInputs.append(Input::NamedPair(makeInput(controller::RY), "RY"));
availableInputs.append(Input::NamedPair(makeInput(controller::RX), "RX"));
// Triggers
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LT), "LT"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RT), "RT"));
availableInputs.append(Input::NamedPair(makeInput(controller::LT), "LT"));
availableInputs.append(Input::NamedPair(makeInput(controller::RT), "RT"));
// Poses
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LEFT), "LeftPose"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RIGHT), "RightPose"));
availableInputs.append(Input::NamedPair(makeInput(controller::LEFT), "LeftPose"));
availableInputs.append(Input::NamedPair(makeInput(controller::RIGHT), "RightPose"));
// Aliases, PlayStation style names
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LB), "L1"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RB), "R1"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LT), "L2"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RT), "R2"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LS), "L3"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RS), "R3"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::BACK), "Select"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::A), "Cross"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::B), "Circle"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::X), "Square"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::Y), "Triangle"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DU), "Up"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DD), "Down"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DL), "Left"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DR), "Right"));
availableInputs.append(Input::NamedPair(makeInput(controller::LB), "L1"));
availableInputs.append(Input::NamedPair(makeInput(controller::RB), "R1"));
availableInputs.append(Input::NamedPair(makeInput(controller::LT), "L2"));
availableInputs.append(Input::NamedPair(makeInput(controller::RT), "R2"));
availableInputs.append(Input::NamedPair(makeInput(controller::LS), "L3"));
availableInputs.append(Input::NamedPair(makeInput(controller::RS), "R3"));
availableInputs.append(Input::NamedPair(makeInput(controller::BACK), "Select"));
availableInputs.append(Input::NamedPair(makeInput(controller::A), "Cross"));
availableInputs.append(Input::NamedPair(makeInput(controller::B), "Circle"));
availableInputs.append(Input::NamedPair(makeInput(controller::X), "Square"));
availableInputs.append(Input::NamedPair(makeInput(controller::Y), "Triangle"));
availableInputs.append(Input::NamedPair(makeInput(controller::DU), "Up"));
availableInputs.append(Input::NamedPair(makeInput(controller::DD), "Down"));
availableInputs.append(Input::NamedPair(makeInput(controller::DL), "Left"));
availableInputs.append(Input::NamedPair(makeInput(controller::DR), "Right"));
return availableInputs;
};
mapper.registerStandardDevice(proxy);
}
void StandardController::assignDefaultInputMapping(UserInputMapper& mapper) {
const float JOYSTICK_MOVE_SPEED = 1.0f;
const float DPAD_MOVE_SPEED = 0.5f;
const float JOYSTICK_YAW_SPEED = 0.5f;
const float JOYSTICK_PITCH_SPEED = 0.25f;
const float BOOM_SPEED = 0.1f;
// Y axes are flipped (up is negative)
// Left Joystick: Movement, strafing
mapper.addInputChannel(UserInputMapper::TRANSLATE_Z, makeInput(controller::LY), JOYSTICK_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::TRANSLATE_X, makeInput(controller::LX), JOYSTICK_MOVE_SPEED);
// Right Joystick: Camera orientation
mapper.addInputChannel(UserInputMapper::YAW, makeInput(controller::RX), JOYSTICK_YAW_SPEED);
mapper.addInputChannel(UserInputMapper::PITCH, makeInput(controller::RY), JOYSTICK_PITCH_SPEED);
// Dpad movement
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(controller::DU), DPAD_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(controller::DD), DPAD_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(controller::DR), DPAD_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(controller::DL), DPAD_MOVE_SPEED);
// Button controls
mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(controller::Y), DPAD_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(controller::X), DPAD_MOVE_SPEED);
// Zoom
mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(controller::RT), BOOM_SPEED);
mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(controller::LB), BOOM_SPEED);
// Hold front right shoulder button for precision controls
// Left Joystick: Movement, strafing
mapper.addInputChannel(UserInputMapper::TRANSLATE_Z, makeInput(controller::LY), makeInput(controller::RB), JOYSTICK_MOVE_SPEED / 2.0f);
mapper.addInputChannel(UserInputMapper::TRANSLATE_X, makeInput(controller::LY), makeInput(controller::RB), JOYSTICK_MOVE_SPEED / 2.0f);
// Right Joystick: Camera orientation
mapper.addInputChannel(UserInputMapper::YAW, makeInput(controller::RX), makeInput(controller::RB), JOYSTICK_YAW_SPEED / 2.0f);
mapper.addInputChannel(UserInputMapper::PITCH, makeInput(controller::RY), makeInput(controller::RB), JOYSTICK_PITCH_SPEED / 2.0f);
// Dpad movement
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(controller::DU), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f);
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(controller::DD), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f);
mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(controller::DR), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f);
mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(controller::DL), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f);
// Button controls
mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(controller::Y), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f);
mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(controller::X), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f);
// Zoom
mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(controller::RT), makeInput(controller::RB), BOOM_SPEED / 2.0f);
mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(controller::LT), makeInput(controller::RB), BOOM_SPEED / 2.0f);
mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(controller::RB));
mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(controller::B));
mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(controller::A));
QString StandardController::getDefaultMappingConfig() {
static const QString DEFAULT_MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/standard.json";
return DEFAULT_MAPPING_JSON;
}
UserInputMapper::Input StandardController::makeInput(controller::StandardButtonChannel button) {
return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON);
}
// FIXME figure out how to move the shifted version to JSON
//void StandardController::assignDefaultInputMapping(UserInputMapper& mapper) {
// const float JOYSTICK_MOVE_SPEED = 1.0f;
// const float DPAD_MOVE_SPEED = 0.5f;
// const float JOYSTICK_YAW_SPEED = 0.5f;
// const float JOYSTICK_PITCH_SPEED = 0.25f;
// const float BOOM_SPEED = 0.1f;
//
// // Hold front right shoulder button for precision controls
// // Left Joystick: Movement, strafing
// mapper.addInputChannel(UserInputMapper::TRANSLATE_Z, makeInput(controller::LY), makeInput(controller::RB), JOYSTICK_MOVE_SPEED / 2.0f);
// mapper.addInputChannel(UserInputMapper::TRANSLATE_X, makeInput(controller::LY), makeInput(controller::RB), JOYSTICK_MOVE_SPEED / 2.0f);
//
// // Right Joystick: Camera orientation
// mapper.addInputChannel(UserInputMapper::YAW, makeInput(controller::RX), makeInput(controller::RB), JOYSTICK_YAW_SPEED / 2.0f);
// mapper.addInputChannel(UserInputMapper::PITCH, makeInput(controller::RY), makeInput(controller::RB), JOYSTICK_PITCH_SPEED / 2.0f);
//
// // Dpad movement
// mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(controller::DU), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f);
// mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(controller::DD), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f);
// mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(controller::DR), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f);
// mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(controller::DL), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f);
//
// // Button controls
// mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(controller::Y), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f);
// mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(controller::X), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f);
//
// // Zoom
// mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(controller::RT), makeInput(controller::RB), BOOM_SPEED / 2.0f);
// mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(controller::LT), makeInput(controller::RB), BOOM_SPEED / 2.0f);
//
// mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(controller::RB));
//
// mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(controller::B));
// mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(controller::A));
//}
UserInputMapper::Input StandardController::makeInput(controller::StandardAxisChannel axis) {
return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS);
}
UserInputMapper::Input StandardController::makeInput(controller::StandardPoseChannel pose) {
return UserInputMapper::Input(_deviceID, pose, UserInputMapper::ChannelType::POSE);
}

View file

@ -12,37 +12,31 @@
#ifndef hifi_StandardController_h
#define hifi_StandardController_h
#include <qobject.h>
#include <qvector.h>
#include <QtCore/QObject>
#include <QtCore/QVector>
#include "InputDevice.h"
#include "StandardControls.h"
typedef std::shared_ptr<StandardController> StandardControllerPointer;
namespace controller {
class StandardController : public QObject, public InputDevice {
Q_OBJECT
Q_PROPERTY(QString name READ getName)
public:
const QString& getName() const { return _name; }
// Device functions
virtual void registerToUserInputMapper(UserInputMapper& mapper) override;
virtual void assignDefaultInputMapping(UserInputMapper& mapper) override;
virtual void buildDeviceProxy(DeviceProxy::Pointer proxy) override;
virtual QString getDefaultMappingConfig() override;
virtual void update(float deltaTime, bool jointsCaptured) override;
virtual void focusOutEvent() override;
StandardController() : InputDevice("Standard") {}
~StandardController();
UserInputMapper::Input makeInput(controller::StandardButtonChannel button);
UserInputMapper::Input makeInput(controller::StandardAxisChannel axis);
UserInputMapper::Input makeInput(controller::StandardPoseChannel pose);
private:
StandardController();
virtual ~StandardController();
};
}
#endif // hifi_StandardController_h

View file

@ -7,20 +7,156 @@
//
#include "UserInputMapper.h"
#include "StandardController.h"
#include <QtCore/QThread>
#include <QtCore/QFile>
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonObject>
#include <QtCore/QJsonArray>
#include <PathUtils.h>
#include "StandardController.h"
#include "Logging.h"
const uint16_t UserInputMapper::ACTIONS_DEVICE = Input::INVALID_DEVICE - (uint16)1;
const uint16_t UserInputMapper::STANDARD_DEVICE = 0;
namespace controller {
const uint16_t UserInputMapper::ACTIONS_DEVICE = Input::INVALID_DEVICE - 0xFF;
const uint16_t UserInputMapper::STANDARD_DEVICE = 0;
}
// Default contruct allocate the poutput size with the current hardcoded action channels
UserInputMapper::UserInputMapper() {
registerStandardDevice();
controller::UserInputMapper::UserInputMapper() {
_activeMappings.push_back(_defaultMapping);
_standardController = std::make_shared<StandardController>();
registerDevice(new ActionsDevice());
registerDevice(_standardController.get());
assignDefaulActionScales();
createActionNames();
}
namespace controller {
class ScriptEndpoint : public Endpoint {
Q_OBJECT;
public:
ScriptEndpoint(const QScriptValue& callable)
: Endpoint(Input::INVALID_INPUT), _callable(callable) {
}
virtual float value();
virtual void apply(float newValue, float oldValue, const Pointer& source);
protected:
Q_INVOKABLE void updateValue();
Q_INVOKABLE virtual void internalApply(float newValue, float oldValue, int sourceID);
private:
QScriptValue _callable;
float _lastValue = 0.0f;
};
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; }
private:
float _currentValue{ 0.0f };
};
class JSEndpoint : public Endpoint {
public:
JSEndpoint(const QJSValue& callable)
: Endpoint(Input::INVALID_INPUT), _callable(callable) {
}
virtual float value() {
float result = (float)_callable.call().toNumber();;
return result;
}
virtual void apply(float newValue, float oldValue, const Pointer& source) {
_callable.call(QJSValueList({ QJSValue(newValue) }));
}
private:
QJSValue _callable;
};
float ScriptEndpoint::value() {
updateValue();
return _lastValue;
}
void ScriptEndpoint::updateValue() {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "updateValue", Qt::QueuedConnection);
return;
}
_lastValue = (float)_callable.call().toNumber();
}
void ScriptEndpoint::apply(float newValue, float oldValue, const Pointer& source) {
internalApply(newValue, oldValue, source->getInput().getID());
}
void ScriptEndpoint::internalApply(float newValue, float oldValue, int sourceID) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "internalApply", Qt::QueuedConnection,
Q_ARG(float, newValue),
Q_ARG(float, oldValue),
Q_ARG(int, sourceID));
return;
}
_callable.call(QScriptValue(),
QScriptValueList({ QScriptValue(newValue), QScriptValue(oldValue), QScriptValue(sourceID) }));
}
class CompositeEndpoint : public Endpoint, Endpoint::Pair {
public:
CompositeEndpoint(Endpoint::Pointer first, Endpoint::Pointer second)
: Endpoint(Input(Input::INVALID_INPUT)), Pair(first, second) { }
virtual float value() {
float result = first->value() * -1.0f + second->value();
return result;
}
virtual void apply(float newValue, float oldValue, const Pointer& source) {
// Composites are read only
}
private:
Endpoint::Pointer _first;
Endpoint::Pointer _second;
};
class ActionEndpoint : public Endpoint {
public:
ActionEndpoint(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;
if (!(_input == Input::INVALID_INPUT)) {
auto userInputMapper = DependencyManager::get<UserInputMapper>();
userInputMapper->deltaActionState(Action(_input.getChannel()), newValue);
}
}
private:
float _currentValue{ 0.0f };
};
UserInputMapper::~UserInputMapper() {
}
@ -32,28 +168,58 @@ int UserInputMapper::recordDeviceOfType(const QString& deviceName) {
return _deviceCounts[deviceName];
}
bool UserInputMapper::registerDevice(uint16 deviceID, const DeviceProxy::Pointer& proxy) {
void UserInputMapper::registerDevice(InputDevice* device) {
if (device->_deviceID == Input::INVALID_DEVICE) {
device->_deviceID = getFreeDeviceID();
}
const auto& deviceID = device->_deviceID;
DeviceProxy::Pointer proxy = std::make_shared<DeviceProxy>();
proxy->_name = device->_name;
device->buildDeviceProxy(proxy);
int numberOfType = recordDeviceOfType(proxy->_name);
if (numberOfType > 1) {
proxy->_name += QString::number(numberOfType);
}
qCDebug(controllers) << "Registered input device <" << proxy->_name << "> deviceID = " << deviceID;
for (const auto& inputMapping : proxy->getAvailabeInputs()) {
const auto& input = inputMapping.first;
// Ignore aliases
if (_endpointsByInput.count(input)) {
continue;
}
Endpoint::Pointer endpoint;
if (input.device == STANDARD_DEVICE) {
endpoint = std::make_shared<VirtualEndpoint>(input);
} else if (input.device == ACTIONS_DEVICE) {
endpoint = std::make_shared<ActionEndpoint>(input);
} else {
endpoint = std::make_shared<LambdaEndpoint>([=] {
return proxy->getValue(input, 0);
});
}
_inputsByEndpoint[endpoint] = input;
_endpointsByInput[input] = endpoint;
}
_registeredDevices[deviceID] = proxy;
return true;
auto mapping = loadMapping(device->getDefaultMappingConfig());
if (mapping) {
_mappingsByDevice[deviceID] = mapping;
for (const auto& entry : mapping->channelMappings) {
const auto& source = entry.first;
const auto& routes = entry.second;
auto& list = _defaultMapping->channelMappings[source];
list.insert(list.end(), routes.begin(), routes.end());
}
}
emit hardwareChanged();
}
bool UserInputMapper::registerStandardDevice(const DeviceProxy::Pointer& device) {
device->_name = "Standard"; // Just to make sure
_registeredDevices[getStandardDeviceID()] = device;
return true;
}
UserInputMapper::DeviceProxy::Pointer UserInputMapper::getDeviceProxy(const Input& input) {
DeviceProxy::Pointer UserInputMapper::getDeviceProxy(const Input& input) {
auto device = _registeredDevices.find(input.getDevice());
if (device != _registeredDevices.end()) {
return (device->second);
@ -69,25 +235,9 @@ QString UserInputMapper::getDeviceName(uint16 deviceID) {
return QString("unknown");
}
void UserInputMapper::resetAllDeviceBindings() {
for (auto device : _registeredDevices) {
device.second->resetDeviceBindings();
}
}
void UserInputMapper::resetDevice(uint16 deviceID) {
auto device = _registeredDevices.find(deviceID);
if (device != _registeredDevices.end()) {
device->second->resetDeviceBindings();
}
}
int UserInputMapper::findDevice(QString name) const {
for (auto device : _registeredDevices) {
if (device.second->_name.split(" (")[0] == name) {
return device.first;
} else if (device.second->_baseName == name) {
if (device.second->_name == name) {
return device.first;
}
}
@ -103,8 +253,11 @@ QVector<QString> UserInputMapper::getDeviceNames() {
return result;
}
UserInputMapper::Input UserInputMapper::findDeviceInput(const QString& inputName) const {
int UserInputMapper::findAction(const QString& actionName) const {
return findDeviceInput("Actions." + actionName).getChannel();
}
Input UserInputMapper::findDeviceInput(const QString& inputName) const {
// Split the full input name as such: deviceName.inputName
auto names = inputName.split('.');
@ -126,20 +279,9 @@ UserInputMapper::Input UserInputMapper::findDeviceInput(const QString& inputName
qCDebug(controllers) << "Couldn\'t find InputChannel named <" << inputName << "> for device <" << deviceName << ">";
} else if (deviceName == "Actions") {
deviceID = ACTIONS_DEVICE;
int actionNum = 0;
for (auto action : _actionNames) {
if (action == inputName) {
return Input(ACTIONS_DEVICE, actionNum, ChannelType::AXIS);
}
actionNum++;
}
qCDebug(controllers) << "Couldn\'t find ActionChannel named <" << inputName << "> among actions";
} else {
qCDebug(controllers) << "Couldn\'t find InputDevice named <" << deviceName << ">";
findDevice(deviceName);
}
} else {
qCDebug(controllers) << "Couldn\'t understand <" << inputName << "> as a valid inputDevice.inputName";
@ -148,100 +290,11 @@ UserInputMapper::Input UserInputMapper::findDeviceInput(const QString& inputName
return Input::INVALID_INPUT;
}
bool UserInputMapper::addInputChannel(Action action, const Input& input, float scale) {
return addInputChannel(action, input, Input(), scale);
}
bool UserInputMapper::addInputChannel(Action action, const Input& input, const Input& modifier, float scale) {
// Check that the device is registered
if (!getDeviceProxy(input)) {
qDebug() << "UserInputMapper::addInputChannel: The input comes from a device #" << input.getDevice() << "is unknown. no inputChannel mapped.";
return false;
}
auto inputChannel = InputChannel(input, modifier, action, scale);
// Insert or replace the input to modifiers
if (inputChannel.hasModifier()) {
auto& modifiers = _inputToModifiersMap[input.getID()];
modifiers.push_back(inputChannel._modifier);
std::sort(modifiers.begin(), modifiers.end());
}
// Now update the action To Inputs side of things
_actionToInputsMap.insert(ActionToInputsMap::value_type(action, inputChannel));
return true;
}
int UserInputMapper::addInputChannels(const InputChannels& channels) {
int nbAdded = 0;
for (auto& channel : channels) {
nbAdded += addInputChannel(channel._action, channel._input, channel._modifier, channel._scale);
}
return nbAdded;
}
bool UserInputMapper::removeInputChannel(InputChannel inputChannel) {
// Remove from Input to Modifiers map
if (inputChannel.hasModifier()) {
_inputToModifiersMap.erase(inputChannel._input.getID());
}
// Remove from Action to Inputs map
std::pair<ActionToInputsMap::iterator, ActionToInputsMap::iterator> ret;
ret = _actionToInputsMap.equal_range(inputChannel._action);
for (ActionToInputsMap::iterator it=ret.first; it!=ret.second; ++it) {
if (it->second == inputChannel) {
_actionToInputsMap.erase(it);
return true;
}
}
return false;
}
void UserInputMapper::removeAllInputChannels() {
_inputToModifiersMap.clear();
_actionToInputsMap.clear();
}
void UserInputMapper::removeAllInputChannelsForDevice(uint16 device) {
QVector<InputChannel> channels = getAllInputsForDevice(device);
for (auto& channel : channels) {
removeInputChannel(channel);
}
}
// FIXME remove the associated device mappings
void UserInputMapper::removeDevice(int device) {
removeAllInputChannelsForDevice((uint16) device);
_registeredDevices.erase(device);
}
int UserInputMapper::getInputChannels(InputChannels& channels) const {
for (auto& channel : _actionToInputsMap) {
channels.push_back(channel.second);
}
return _actionToInputsMap.size();
}
QVector<UserInputMapper::InputChannel> UserInputMapper::getAllInputsForDevice(uint16 device) {
InputChannels allChannels;
getInputChannels(allChannels);
QVector<InputChannel> channels;
for (InputChannel inputChannel : allChannels) {
if (inputChannel._input._device == device) {
channels.push_back(inputChannel);
}
}
return channels;
}
void fixBisectedAxis(float& full, float& negative, float& positive) {
full = full + (negative * -1.0f) + positive;
negative = full >= 0.0f ? 0.0f : full * -1.0f;
@ -249,64 +302,17 @@ void fixBisectedAxis(float& full, float& negative, float& positive) {
}
void UserInputMapper::update(float deltaTime) {
// Reset the axis state for next loop
for (auto& channel : _actionStates) {
channel = 0.0f;
}
for (auto& channel : _poseStates) {
channel = PoseValue();
channel = Pose();
}
int currentTimestamp = 0;
for (auto& channelInput : _actionToInputsMap) {
auto& inputMapping = channelInput.second;
auto& inputID = inputMapping._input;
bool enabled = true;
// Check if this input channel has modifiers and collect the possibilities
auto modifiersIt = _inputToModifiersMap.find(inputID.getID());
if (modifiersIt != _inputToModifiersMap.end()) {
Modifiers validModifiers;
bool isActiveModifier = false;
for (auto& modifier : modifiersIt->second) {
auto deviceProxy = getDeviceProxy(modifier);
if (deviceProxy->getButton(modifier, currentTimestamp)) {
validModifiers.push_back(modifier);
isActiveModifier |= (modifier.getID() == inputMapping._modifier.getID());
}
}
enabled = (validModifiers.empty() && !inputMapping.hasModifier()) || isActiveModifier;
}
// if enabled: default input or all modifiers on
if (enabled) {
auto deviceProxy = getDeviceProxy(inputID);
switch (inputMapping._input.getType()) {
case ChannelType::BUTTON: {
_actionStates[channelInput.first] += inputMapping._scale * float(deviceProxy->getButton(inputID, currentTimestamp));// * deltaTime; // weight the impulse by the deltaTime
break;
}
case ChannelType::AXIS: {
_actionStates[channelInput.first] += inputMapping._scale * deviceProxy->getAxis(inputID, currentTimestamp);
break;
}
case ChannelType::POSE: {
if (!_poseStates[channelInput.first].isValid()) {
_poseStates[channelInput.first] = deviceProxy->getPose(inputID, currentTimestamp);
}
break;
}
default: {
break; //silence please
}
}
} else{
// Channel input not enabled
enabled = false;
}
}
// Run the mappings code
update();
// Scale all the channel step with the scale
for (auto i = 0; i < NUM_ACTIONS; i++) {
@ -337,7 +343,12 @@ void UserInputMapper::update(float deltaTime) {
}
}
QVector<UserInputMapper::Action> UserInputMapper::getAllActions() const {
Input::NamedVector UserInputMapper::getAvailableInputs(uint16 deviceID) const {
auto iterator = _registeredDevices.find(deviceID);
return iterator->second->getAvailabeInputs();
}
QVector<Action> UserInputMapper::getAllActions() const {
QVector<Action> actions;
for (auto i = 0; i < NUM_ACTIONS; i++) {
actions.append(Action(i));
@ -345,31 +356,20 @@ QVector<UserInputMapper::Action> UserInputMapper::getAllActions() const {
return actions;
}
QVector<UserInputMapper::InputChannel> UserInputMapper::getInputChannelsForAction(UserInputMapper::Action action) {
QVector<InputChannel> inputChannels;
std::pair <ActionToInputsMap::iterator, ActionToInputsMap::iterator> ret;
ret = _actionToInputsMap.equal_range(action);
for (ActionToInputsMap::iterator it=ret.first; it!=ret.second; ++it) {
inputChannels.append(it->second);
}
return inputChannels;
}
int UserInputMapper::findAction(const QString& actionName) const {
auto actions = getAllActions();
for (auto action : actions) {
if (getActionName(action) == actionName) {
return action;
QString UserInputMapper::getActionName(Action action) const {
for (auto actionPair : getActionInputs()) {
if (actionPair.first.channel == action) {
return actionPair.second;
}
}
// If the action isn't found, return -1
return -1;
return QString();
}
QVector<QString> UserInputMapper::getActionNames() const {
QVector<QString> result;
for (auto i = 0; i < NUM_ACTIONS; i++) {
result << _actionNames[i];
for (auto actionPair : getActionInputs()) {
result << actionPair.second;
}
return result;
}
@ -402,56 +402,18 @@ void UserInputMapper::assignDefaulActionScales() {
_actionScales[YAW] = 1.0f; // default
}
// This is only necessary as long as the actions are hardcoded
// Eventually you can just add the string when you add the action
void UserInputMapper::createActionNames() {
_actionNames[LONGITUDINAL_BACKWARD] = "LONGITUDINAL_BACKWARD";
_actionNames[LONGITUDINAL_FORWARD] = "LONGITUDINAL_FORWARD";
_actionNames[LATERAL_LEFT] = "LATERAL_LEFT";
_actionNames[LATERAL_RIGHT] = "LATERAL_RIGHT";
_actionNames[VERTICAL_DOWN] = "VERTICAL_DOWN";
_actionNames[VERTICAL_UP] = "VERTICAL_UP";
_actionNames[YAW_LEFT] = "YAW_LEFT";
_actionNames[YAW_RIGHT] = "YAW_RIGHT";
_actionNames[PITCH_DOWN] = "PITCH_DOWN";
_actionNames[PITCH_UP] = "PITCH_UP";
_actionNames[BOOM_IN] = "BOOM_IN";
_actionNames[BOOM_OUT] = "BOOM_OUT";
_actionNames[LEFT_HAND] = "LEFT_HAND";
_actionNames[RIGHT_HAND] = "RIGHT_HAND";
_actionNames[LEFT_HAND_CLICK] = "LEFT_HAND_CLICK";
_actionNames[RIGHT_HAND_CLICK] = "RIGHT_HAND_CLICK";
_actionNames[SHIFT] = "SHIFT";
_actionNames[ACTION1] = "ACTION1";
_actionNames[ACTION2] = "ACTION2";
_actionNames[CONTEXT_MENU] = "CONTEXT_MENU";
_actionNames[TOGGLE_MUTE] = "TOGGLE_MUTE";
_actionNames[TRANSLATE_X] = "TranslateX";
_actionNames[TRANSLATE_Y] = "TranslateY";
_actionNames[TRANSLATE_Z] = "TranslateZ";
_actionNames[ROLL] = "Roll";
_actionNames[PITCH] = "Pitch";
_actionNames[YAW] = "Yaw";
}
static int actionMetaTypeId = qRegisterMetaType<Action>();
static int inputMetaTypeId = qRegisterMetaType<Input>();
static int inputPairMetaTypeId = qRegisterMetaType<InputPair>();
void UserInputMapper::registerStandardDevice() {
_standardController = std::make_shared<StandardController>();
_standardController->registerToUserInputMapper(*this);
_standardController->assignDefaultInputMapping(*this);
}
QScriptValue inputToScriptValue(QScriptEngine* engine, const Input& input);
void inputFromScriptValue(const QScriptValue& object, Input& input);
QScriptValue actionToScriptValue(QScriptEngine* engine, const Action& action);
void actionFromScriptValue(const QScriptValue& object, Action& action);
QScriptValue inputPairToScriptValue(QScriptEngine* engine, const InputPair& inputPair);
void inputPairFromScriptValue(const QScriptValue& object, InputPair& inputPair);
static int actionMetaTypeId = qRegisterMetaType<UserInputMapper::Action>();
static int inputMetaTypeId = qRegisterMetaType<UserInputMapper::Input>();
static int inputPairMetaTypeId = qRegisterMetaType<UserInputMapper::InputPair>();
QScriptValue inputToScriptValue(QScriptEngine* engine, const UserInputMapper::Input& input);
void inputFromScriptValue(const QScriptValue& object, UserInputMapper::Input& input);
QScriptValue actionToScriptValue(QScriptEngine* engine, const UserInputMapper::Action& action);
void actionFromScriptValue(const QScriptValue& object, UserInputMapper::Action& action);
QScriptValue inputPairToScriptValue(QScriptEngine* engine, const UserInputMapper::InputPair& inputPair);
void inputPairFromScriptValue(const QScriptValue& object, UserInputMapper::InputPair& inputPair);
QScriptValue inputToScriptValue(QScriptEngine* engine, const UserInputMapper::Input& input) {
QScriptValue inputToScriptValue(QScriptEngine* engine, const Input& input) {
QScriptValue obj = engine->newObject();
obj.setProperty("device", input.getDevice());
obj.setProperty("channel", input.getChannel());
@ -460,14 +422,11 @@ QScriptValue inputToScriptValue(QScriptEngine* engine, const UserInputMapper::In
return obj;
}
void inputFromScriptValue(const QScriptValue& object, UserInputMapper::Input& input) {
input.setDevice(object.property("device").toUInt16());
input.setChannel(object.property("channel").toUInt16());
input.setType(object.property("type").toUInt16());
input.setID(object.property("id").toInt32());
void inputFromScriptValue(const QScriptValue& object, Input& input) {
input.id = object.property("id").toInt32();
}
QScriptValue actionToScriptValue(QScriptEngine* engine, const UserInputMapper::Action& action) {
QScriptValue actionToScriptValue(QScriptEngine* engine, const Action& action) {
QScriptValue obj = engine->newObject();
auto userInputMapper = DependencyManager::get<UserInputMapper>();
obj.setProperty("action", (int)action);
@ -475,38 +434,361 @@ QScriptValue actionToScriptValue(QScriptEngine* engine, const UserInputMapper::A
return obj;
}
void actionFromScriptValue(const QScriptValue& object, UserInputMapper::Action& action) {
action = UserInputMapper::Action(object.property("action").toVariant().toInt());
void actionFromScriptValue(const QScriptValue& object, Action& action) {
action = Action(object.property("action").toVariant().toInt());
}
QScriptValue inputPairToScriptValue(QScriptEngine* engine, const UserInputMapper::InputPair& inputPair) {
QScriptValue inputPairToScriptValue(QScriptEngine* engine, const InputPair& inputPair) {
QScriptValue obj = engine->newObject();
obj.setProperty("input", inputToScriptValue(engine, inputPair.first));
obj.setProperty("inputName", inputPair.second);
return obj;
}
void inputPairFromScriptValue(const QScriptValue& object, UserInputMapper::InputPair& inputPair) {
void inputPairFromScriptValue(const QScriptValue& object, InputPair& inputPair) {
inputFromScriptValue(object.property("input"), inputPair.first);
inputPair.second = QString(object.property("inputName").toVariant().toString());
}
void UserInputMapper::registerControllerTypes(QScriptEngine* engine) {
qScriptRegisterSequenceMetaType<QVector<UserInputMapper::Action> >(engine);
qScriptRegisterSequenceMetaType<QVector<UserInputMapper::InputPair> >(engine);
qScriptRegisterSequenceMetaType<QVector<Action> >(engine);
qScriptRegisterSequenceMetaType<QVector<InputPair> >(engine);
qScriptRegisterMetaType(engine, actionToScriptValue, actionFromScriptValue);
qScriptRegisterMetaType(engine, inputToScriptValue, inputFromScriptValue);
qScriptRegisterMetaType(engine, inputPairToScriptValue, inputPairFromScriptValue);
}
UserInputMapper::Input UserInputMapper::makeStandardInput(controller::StandardButtonChannel button) {
Input UserInputMapper::makeStandardInput(controller::StandardButtonChannel button) {
return Input(STANDARD_DEVICE, button, ChannelType::BUTTON);
}
UserInputMapper::Input UserInputMapper::makeStandardInput(controller::StandardAxisChannel axis) {
Input UserInputMapper::makeStandardInput(controller::StandardAxisChannel axis) {
return Input(STANDARD_DEVICE, axis, ChannelType::AXIS);
}
UserInputMapper::Input UserInputMapper::makeStandardInput(controller::StandardPoseChannel pose) {
Input UserInputMapper::makeStandardInput(controller::StandardPoseChannel pose) {
return Input(STANDARD_DEVICE, pose, ChannelType::POSE);
}
void UserInputMapper::update() {
static auto deviceNames = getDeviceNames();
_overrideValues.clear();
EndpointSet readEndpoints;
EndpointSet writtenEndpoints;
static const int HARDWARE_PASS = 0;
static const int STANDARD_PASS = 1;
// Now process the current values for each level of the stack
for (auto& mapping : _activeMappings) {
for (int pass = 0; pass < 2; ++pass) {
for (const auto& mappingEntry : mapping->channelMappings) {
const auto& source = mappingEntry.first;
if (_inputsByEndpoint.count(source)) {
auto sourceInput = _inputsByEndpoint[source];
if ((sourceInput.device == STANDARD_DEVICE) ^ (pass == STANDARD_PASS)) {
continue;
}
}
// 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;
}
// Apply the value to all the routes
const auto& routes = mappingEntry.second;
for (const auto& route : routes) {
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;
}
// 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
float value = getValue(source);
// Apply each of the filters.
const auto& filters = route->filters;
for (const auto& filter : route->filters) {
value = filter->apply(value);
}
if (loopback) {
_overrideValues[source] = value;
} else {
destination->apply(value, 0, source);
}
}
}
}
}
}
Endpoint::Pointer UserInputMapper::endpointFor(const QJSValue& endpoint) {
if (endpoint.isNumber()) {
return endpointFor(Input(endpoint.toInt()));
}
if (endpoint.isCallable()) {
auto result = std::make_shared<JSEndpoint>(endpoint);
return result;
}
qWarning() << "Unsupported input type " << endpoint.toString();
return Endpoint::Pointer();
}
Endpoint::Pointer UserInputMapper::endpointFor(const QScriptValue& endpoint) {
if (endpoint.isNumber()) {
return endpointFor(Input(endpoint.toInt32()));
}
if (endpoint.isFunction()) {
auto result = std::make_shared<ScriptEndpoint>(endpoint);
return result;
}
qWarning() << "Unsupported input type " << endpoint.toString();
return Endpoint::Pointer();
}
Endpoint::Pointer UserInputMapper::endpointFor(const Input& inputId) const {
auto iterator = _endpointsByInput.find(inputId);
if (_endpointsByInput.end() == iterator) {
qWarning() << "Unknown input: " << QString::number(inputId.getID(), 16);
return Endpoint::Pointer();
}
return iterator->second;
}
Endpoint::Pointer UserInputMapper::compositeEndpointFor(Endpoint::Pointer first, Endpoint::Pointer second) {
EndpointPair pair(first, second);
Endpoint::Pointer result;
auto iterator = _compositeEndpoints.find(pair);
if (_compositeEndpoints.end() == iterator) {
result = std::make_shared<CompositeEndpoint>(first, second);
_compositeEndpoints[pair] = result;
} else {
result = iterator->second;
}
return result;
}
Mapping::Pointer UserInputMapper::newMapping(const QString& mappingName) {
if (_mappingsByName.count(mappingName)) {
qCWarning(controllers) << "Refusing to recreate mapping named " << mappingName;
}
qDebug() << "Creating new Mapping " << mappingName;
auto mapping = std::make_shared<Mapping>(mappingName);
_mappingsByName[mappingName] = mapping;
return mapping;
}
// FIXME handle asynchronous loading in the UserInputMapper
//QObject* ScriptingInterface::loadMapping(const QString& jsonUrl) {
// QObject* result = nullptr;
// auto request = ResourceManager::createResourceRequest(nullptr, QUrl(jsonUrl));
// if (request) {
// QEventLoop eventLoop;
// request->setCacheEnabled(false);
// connect(request, &ResourceRequest::finished, &eventLoop, &QEventLoop::quit);
// request->send();
// if (request->getState() != ResourceRequest::Finished) {
// eventLoop.exec();
// }
//
// if (request->getResult() == ResourceRequest::Success) {
// result = parseMapping(QString(request->getData()));
// } else {
// qCWarning(controllers) << "Failed to load mapping url <" << jsonUrl << ">" << endl;
// }
// request->deleteLater();
// }
// return result;
//}
void UserInputMapper::enableMapping(const QString& mappingName, bool enable) {
qCDebug(controllers) << "Attempting to enable mapping " << mappingName;
auto iterator = _mappingsByName.find(mappingName);
if (_mappingsByName.end() == iterator) {
qCWarning(controllers) << "Request to enable / disable unknown mapping " << mappingName;
return;
}
auto mapping = iterator->second;
if (enable) {
_activeMappings.push_front(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);
}
}
float UserInputMapper::getValue(const Endpoint::Pointer& endpoint) const {
auto valuesIterator = _overrideValues.find(endpoint);
if (_overrideValues.end() != valuesIterator) {
return valuesIterator->second;
}
return endpoint->value();
}
float UserInputMapper::getValue(const Input& input) const {
auto endpoint = endpointFor(input);
if (!endpoint) {
return 0;
}
return endpoint->value();
}
Mapping::Pointer UserInputMapper::loadMapping(const QString& jsonFile) {
if (jsonFile.isEmpty()) {
return Mapping::Pointer();
}
QString json;
{
QFile file(jsonFile);
if (file.open(QFile::ReadOnly | QFile::Text)) {
json = QTextStream(&file).readAll();
}
file.close();
}
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_TO = QStringLiteral("to");
const QString JSON_CHANNEL_FILTERS = QStringLiteral("filters");
Endpoint::Pointer UserInputMapper::parseEndpoint(const QJsonValue& value) {
if (value.isString()) {
auto input = findDeviceInput(value.toString());
return endpointFor(input);
} else if (value.isObject()) {
// Endpoint is defined as an object, we expect a js function then
return Endpoint::Pointer();
}
return Endpoint::Pointer();
}
Route::Pointer UserInputMapper::parseRoute(const QJsonValue& value) {
if (!value.isObject()) {
return Route::Pointer();
}
const auto& obj = value.toObject();
Route::Pointer result = std::make_shared<Route>();
result->source = parseEndpoint(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]);
if (!result->destination) {
qWarning() << "Invalid route destination " << obj[JSON_CHANNEL_TO];
return Route::Pointer();
}
const auto& filtersValue = obj[JSON_CHANNEL_FILTERS];
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);
}
}
return result;
}
Mapping::Pointer UserInputMapper::parseMapping(const QJsonValue& json) {
if (!json.isObject()) {
return Mapping::Pointer();
}
auto obj = json.toObject();
auto mapping = std::make_shared<Mapping>("default");
mapping->name = obj[JSON_NAME].toString();
mapping->channelMappings.clear();
const auto& jsonChannels = obj[JSON_CHANNELS].toArray();
Mapping::Map map;
for (const auto& channelIt : jsonChannels) {
Route::Pointer route = parseRoute(channelIt);
if (!route) {
qWarning() << "Couldn't parse route";
continue;
}
mapping->channelMappings[route->source].push_back(route);
}
return mapping;
}
Mapping::Pointer UserInputMapper::parseMapping(const QString& json) {
Mapping::Pointer result;
QJsonObject obj;
QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson(json.toUtf8(), &error);
// check validity of the document
if (doc.isNull()) {
return Mapping::Pointer();
}
if (!doc.isObject()) {
qWarning() << "Mapping json Document is not an object" << endl;
return Mapping::Pointer();
}
// FIXME how did we detect this?
// qDebug() << "Invalid JSON...\n";
// qDebug() << error.errorString();
// qDebug() << "JSON was:\n" << json << endl;
//}
return parseMapping(doc.object());
}
}
#include "UserInputMapper.moc"

View file

@ -15,234 +15,155 @@
#include <unordered_set>
#include <functional>
#include <memory>
#include <QtQml/QJSValue>
#include <QtScript/QScriptValue>
#include <DependencyManager.h>
#include <RegisteredMetaTypes.h>
#include "Pose.h"
#include "Input.h"
#include "InputDevice.h"
#include "DeviceProxy.h"
#include "StandardControls.h"
#include "Mapping.h"
#include "Endpoint.h"
#include "Actions.h"
class StandardController;
typedef std::shared_ptr<StandardController> StandardControllerPointer;
namespace controller {
class UserInputMapper : public QObject, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY
Q_ENUMS(Action)
class UserInputMapper : public QObject, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY
Q_ENUMS(Action)
public:
~UserInputMapper();
using DeviceProxy = controller::DeviceProxy;
using PoseValue = controller::Pose;
using Input = controller::Input;
using ChannelType = controller::ChannelType;
typedef unsigned short uint16;
typedef unsigned int uint32;
static void registerControllerTypes(QScriptEngine* engine);
static const uint16 ACTIONS_DEVICE;
static const uint16 STANDARD_DEVICE;
// Modifiers are just button inputID
typedef std::vector< Input > Modifiers;
typedef std::function<bool (const Input& input, int timestamp)> ButtonGetter;
typedef std::function<float (const Input& input, int timestamp)> AxisGetter;
typedef std::function<PoseValue (const Input& input, int timestamp)> PoseGetter;
typedef QPair<Input, QString> InputPair;
typedef std::function<QVector<InputPair> ()> AvailableInputGetter;
typedef std::function<bool ()> ResetBindings;
typedef QVector<InputPair> AvailableInput;
// GetFreeDeviceID should be called before registering a device to use an ID not used by a different device.
uint16 getFreeDeviceID() { return _nextFreeDeviceID++; }
bool registerDevice(uint16 deviceID, const DeviceProxy::Pointer& device);
bool registerStandardDevice(const DeviceProxy::Pointer& device);
DeviceProxy::Pointer getDeviceProxy(const Input& input);
QString getDeviceName(uint16 deviceID);
QVector<InputPair> getAvailableInputs(uint16 deviceID) { return _registeredDevices[deviceID]->getAvailabeInputs(); }
void resetAllDeviceBindings();
void resetDevice(uint16 deviceID);
int findDevice(QString name) const;
QVector<QString> getDeviceNames();
Input findDeviceInput(const QString& inputName) const;
// Actions are the output channels of the Mapper, that's what the InputChannel map to
// For now the Actions are hardcoded, this is bad, but we will fix that in the near future
enum Action {
TRANSLATE_X = 0,
TRANSLATE_Y,
TRANSLATE_Z,
ROTATE_X, PITCH = ROTATE_X,
ROTATE_Y, YAW = ROTATE_Y,
ROTATE_Z, ROLL = ROTATE_Z,
TRANSLATE_CAMERA_Z,
NUM_COMBINED_AXES,
LEFT_HAND = NUM_COMBINED_AXES,
RIGHT_HAND,
LEFT_HAND_CLICK,
RIGHT_HAND_CLICK,
ACTION1,
ACTION2,
CONTEXT_MENU,
TOGGLE_MUTE,
SHIFT,
// Biseced aliases for TRANSLATE_Z
LONGITUDINAL_BACKWARD,
LONGITUDINAL_FORWARD,
// Biseced aliases for TRANSLATE_X
LATERAL_LEFT,
LATERAL_RIGHT,
// Biseced aliases for TRANSLATE_Y
VERTICAL_DOWN,
VERTICAL_UP,
// Biseced aliases for ROTATE_Y
YAW_LEFT,
YAW_RIGHT,
// Biseced aliases for ROTATE_X
PITCH_DOWN,
PITCH_UP,
// Biseced aliases for TRANSLATE_CAMERA_Z
BOOM_IN,
BOOM_OUT,
NUM_ACTIONS,
};
std::vector<QString> _actionNames = std::vector<QString>(NUM_ACTIONS);
void createActionNames();
QVector<Action> getAllActions() const;
QString getActionName(Action action) const { return UserInputMapper::_actionNames[(int) action]; }
float getActionState(Action action) const { return _actionStates[action]; }
PoseValue getPoseState(Action action) const { return _poseStates[action]; }
int findAction(const QString& actionName) const;
QVector<QString> getActionNames() const;
void assignDefaulActionScales();
void setActionState(Action action, float value) { _externalActionStates[action] = value; }
void deltaActionState(Action action, float delta) { _externalActionStates[action] += delta; }
// Add input channel to the mapper and check that all the used channels are registered.
// Return true if theinput channel is created correctly, false either
bool addInputChannel(Action action, const Input& input, float scale = 1.0f);
bool addInputChannel(Action action, const Input& input, const Input& modifer, float scale = 1.0f);
UserInputMapper::Input makeStandardInput(controller::StandardButtonChannel button);
UserInputMapper::Input makeStandardInput(controller::StandardAxisChannel axis);
UserInputMapper::Input makeStandardInput(controller::StandardPoseChannel pose);
// Under the hood, the input channels are organized in map sorted on the _output
// The InputChannel class is just the full values describing the input channel in one object
class InputChannel {
public:
Input _input;
Input _modifier = Input(); // make it invalid by default, meaning no modifier
Action _action = LONGITUDINAL_BACKWARD;
float _scale = 0.0f;
Input getInput() const { return _input; }
Input getModifier() const { return _modifier; }
Action getAction() const { return _action; }
float getScale() const { return _scale; }
void setInput(Input input) { _input = input; }
void setModifier(Input modifier) { _modifier = modifier; }
void setAction(Action action) { _action = action; }
void setScale(float scale) { _scale = scale; }
using InputPair = Input::NamedPair;
// FIXME move to unordered set / map
using EndpointToInputMap = std::map<Endpoint::Pointer, Input>;
using MappingNameMap = std::map<QString, Mapping::Pointer>;
using MappingDeviceMap = std::map<uint16_t, Mapping::Pointer>;
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 EndpointPair = std::pair<Endpoint::Pointer, Endpoint::Pointer>;
using EndpointPairMap = std::map<EndpointPair, Endpoint::Pointer>;
using DevicesMap = std::map<int, DeviceProxy::Pointer>;
using uint16 = uint16_t;
using uint32 = uint32_t;
InputChannel() {}
InputChannel(const Input& input, const Input& modifier, Action action, float scale = 1.0f) :
_input(input), _modifier(modifier), _action(action), _scale(scale) {}
InputChannel(const InputChannel& src) : InputChannel(src._input, src._modifier, src._action, src._scale) {}
InputChannel& operator = (const InputChannel& src) { _input = src._input; _modifier = src._modifier; _action = src._action; _scale = src._scale; return (*this); }
bool operator ==(const InputChannel& right) const { return _input == right._input && _modifier == right._modifier && _action == right._action && _scale == right._scale; }
bool hasModifier() { return _modifier.isValid(); }
static const uint16_t ACTIONS_DEVICE;
static const uint16_t STANDARD_DEVICE;
UserInputMapper();
virtual ~UserInputMapper();
static void registerControllerTypes(QScriptEngine* engine);
void registerDevice(InputDevice* device);
DeviceProxy::Pointer getDeviceProxy(const Input& input);
QString getDeviceName(uint16 deviceID);
Input::NamedVector getAvailableInputs(uint16 deviceID) const;
Input::NamedVector getActionInputs() const { return getAvailableInputs(ACTIONS_DEVICE); }
Input::NamedVector getStandardInputs() const { return getAvailableInputs(STANDARD_DEVICE); }
int findDevice(QString name) const;
QVector<QString> getDeviceNames();
Input findDeviceInput(const QString& inputName) const;
QVector<Action> getAllActions() const;
QString getActionName(Action action) const;
float getActionState(Action action) const { return _actionStates[action]; }
Pose getPoseState(Action action) const { return _poseStates[action]; }
int findAction(const QString& actionName) const;
QVector<QString> getActionNames() const;
void assignDefaulActionScales();
void setActionState(Action action, float value) { _externalActionStates[action] = value; }
void deltaActionState(Action action, float delta) { _externalActionStates[action] += delta; }
static Input makeStandardInput(controller::StandardButtonChannel button);
static Input makeStandardInput(controller::StandardAxisChannel axis);
static Input makeStandardInput(controller::StandardPoseChannel pose);
void removeDevice(int device);
// Update means go grab all the device input channels and update the output channel values
void update(float deltaTime);
void setSensorToWorldMat(glm::mat4 sensorToWorldMat) { _sensorToWorldMat = sensorToWorldMat; }
glm::mat4 getSensorToWorldMat() { return _sensorToWorldMat; }
DevicesMap getDevices() { return _registeredDevices; }
uint16 getStandardDeviceID() const { return STANDARD_DEVICE; }
DeviceProxy::Pointer getStandardDevice() { return _registeredDevices[getStandardDeviceID()]; }
Mapping::Pointer newMapping(const QString& mappingName);
Mapping::Pointer parseMapping(const QString& json);
Mapping::Pointer loadMapping(const QString& jsonFile);
void enableMapping(const QString& mappingName, bool enable = true);
float getValue(const Input& input) const;
signals:
void actionEvent(int action, float state);
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>(NUM_ACTIONS, 0.0f);
std::vector<float> _externalActionStates = std::vector<float>(NUM_ACTIONS, 0.0f);
std::vector<float> _actionScales = std::vector<float>(NUM_ACTIONS, 1.0f);
std::vector<float> _lastActionStates = std::vector<float>(NUM_ACTIONS, 0.0f);
std::vector<Pose> _poseStates = std::vector<Pose>(NUM_ACTIONS);
glm::mat4 _sensorToWorldMat;
int recordDeviceOfType(const QString& deviceName);
QHash<const QString&, int> _deviceCounts;
float getValue(const Endpoint::Pointer& endpoint) const;
friend class RouteBuilderProxy;
friend class MappingBuilderProxy;
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);
Endpoint::Pointer parseEndpoint(const QJsonValue& value);
InputToEndpointMap _endpointsByInput;
EndpointToInputMap _inputsByEndpoint;
EndpointPairMap _compositeEndpoints;
ValueMap _overrideValues;
MappingNameMap _mappingsByName;
Mapping::Pointer _defaultMapping{ std::make_shared<Mapping>("Default") };
MappingDeviceMap _mappingsByDevice;
MappingStack _activeMappings;
};
typedef std::vector< InputChannel > InputChannels;
}
// Add a bunch of input channels, return the true number of channels that successfully were added
int addInputChannels(const InputChannels& channels);
// Remove the first found instance of the input channel from the input mapper, true if found
bool removeInputChannel(InputChannel channel);
void removeAllInputChannels();
void removeAllInputChannelsForDevice(uint16 device);
void removeDevice(int device);
//Grab all the input channels currently in use, return the number
int getInputChannels(InputChannels& channels) const;
QVector<InputChannel> getAllInputsForDevice(uint16 device);
QVector<InputChannel> getInputChannelsForAction(UserInputMapper::Action action);
std::multimap<Action, InputChannel> getActionToInputsMap() { return _actionToInputsMap; }
Q_DECLARE_METATYPE(controller::UserInputMapper::InputPair)
Q_DECLARE_METATYPE(controller::Pose)
Q_DECLARE_METATYPE(QVector<controller::UserInputMapper::InputPair>)
Q_DECLARE_METATYPE(controller::Input)
Q_DECLARE_METATYPE(controller::Action)
Q_DECLARE_METATYPE(QVector<controller::Action>)
// Update means go grab all the device input channels and update the output channel values
void update(float deltaTime);
void setSensorToWorldMat(glm::mat4 sensorToWorldMat) { _sensorToWorldMat = sensorToWorldMat; }
glm::mat4 getSensorToWorldMat() { return _sensorToWorldMat; }
UserInputMapper();
typedef std::map<int, DeviceProxy::Pointer> DevicesMap;
DevicesMap getDevices() { return _registeredDevices; }
uint16 getStandardDeviceID() const { return STANDARD_DEVICE; }
DeviceProxy::Pointer getStandardDevice() { return _registeredDevices[getStandardDeviceID()]; }
signals:
void actionEvent(int action, float state);
protected:
void registerStandardDevice();
StandardControllerPointer _standardController;
DevicesMap _registeredDevices;
uint16 _nextFreeDeviceID = STANDARD_DEVICE + 1;
typedef std::map<int, Modifiers> InputToMoModifiersMap;
InputToMoModifiersMap _inputToModifiersMap;
typedef std::multimap<Action, InputChannel> ActionToInputsMap;
ActionToInputsMap _actionToInputsMap;
std::vector<float> _actionStates = std::vector<float>(NUM_ACTIONS, 0.0f);
std::vector<float> _externalActionStates = std::vector<float>(NUM_ACTIONS, 0.0f);
std::vector<float> _actionScales = std::vector<float>(NUM_ACTIONS, 1.0f);
std::vector<float> _lastActionStates = std::vector<float>(NUM_ACTIONS, 0.0f);
std::vector<PoseValue> _poseStates = std::vector<PoseValue>(NUM_ACTIONS);
glm::mat4 _sensorToWorldMat;
int recordDeviceOfType(const QString& deviceName);
QHash<const QString&, int> _deviceCounts;
};
Q_DECLARE_METATYPE(UserInputMapper::InputPair)
Q_DECLARE_METATYPE(UserInputMapper::PoseValue)
Q_DECLARE_METATYPE(QVector<UserInputMapper::InputPair>)
Q_DECLARE_METATYPE(UserInputMapper::Input)
Q_DECLARE_METATYPE(UserInputMapper::InputChannel)
Q_DECLARE_METATYPE(QVector<UserInputMapper::InputChannel>)
Q_DECLARE_METATYPE(UserInputMapper::Action)
Q_DECLARE_METATYPE(QVector<UserInputMapper::Action>)
// Cheating.
using UserInputMapper = controller::UserInputMapper;
#endif // hifi_UserInputMapper_h

View file

@ -22,7 +22,7 @@ using namespace controller;
QObject* MappingBuilderProxy::from(int input) {
qCDebug(controllers) << "Creating new Route builder proxy from " << input;
auto sourceEndpoint = _parent.endpointFor(UserInputMapper::Input(input));
auto sourceEndpoint = _parent.endpointFor(Input(input));
return from(sourceEndpoint);
}
@ -41,7 +41,7 @@ QObject* MappingBuilderProxy::from(const QScriptValue& source) {
QObject* MappingBuilderProxy::from(const Endpoint::Pointer& source) {
if (source) {
auto route = Route::Pointer(new Route());
route->_source = source;
route->source = source;
return new RouteBuilderProxy(_parent, _mapping, route);
} else {
qCDebug(controllers) << "MappingBuilderProxy::from : source is null so no route created";
@ -55,48 +55,8 @@ QObject* MappingBuilderProxy::makeAxis(const QJSValue& source1, const QJSValue&
return from(_parent.compositeEndpointFor(source1Endpoint, source2Endpoint));
}
const QString JSON_NAME = QStringLiteral("name");
const QString JSON_CHANNELS = QStringLiteral("channels");
const QString JSON_CHANNEL_FROM = QStringLiteral("from");
const QString JSON_CHANNEL_TO = QStringLiteral("to");
const QString JSON_CHANNEL_FILTERS = QStringLiteral("filters");
void MappingBuilderProxy::parse(const QJsonObject& json) {
_mapping->_name = json[JSON_NAME].toString();
_mapping->_channelMappings.clear();
const auto& jsonChannels = json[JSON_CHANNELS].toArray();
for (const auto& channelIt : jsonChannels) {
parseRoute(channelIt);
}
}
void MappingBuilderProxy::parseRoute(const QJsonValue& json) {
if (json.isObject()) {
const auto& jsonChannel = json.toObject();
auto newRoute = from(jsonChannel[JSON_CHANNEL_FROM]);
if (newRoute) {
auto route = dynamic_cast<RouteBuilderProxy*>(newRoute);
route->filters(jsonChannel[JSON_CHANNEL_FILTERS]);
route->to(jsonChannel[JSON_CHANNEL_TO]);
}
}
}
QObject* MappingBuilderProxy::from(const QJsonValue& json) {
if (json.isString()) {
return from(_parent.endpointFor(_parent.inputFor(json.toString())));
} else if (json.isObject()) {
// Endpoint is defined as an object, we expect a js function then
return nullptr;
}
return nullptr;
}
QObject* MappingBuilderProxy::enable(bool enable) {
_parent.enableMapping(_mapping->_name, enable);
_parent.enableMapping(_mapping->name, enable);
return this;
}

View file

@ -22,13 +22,14 @@ class QJsonValue;
namespace controller {
class ScriptingInterface;
class UserInputMapper;
// TODO migrate functionality to a MappingBuilder class and make the proxy defer to that
// (for easier use in both C++ and JS)
class MappingBuilderProxy : public QObject {
Q_OBJECT
public:
MappingBuilderProxy(ScriptingInterface& parent, Mapping::Pointer mapping)
MappingBuilderProxy(UserInputMapper& parent, Mapping::Pointer mapping)
: _parent(parent), _mapping(mapping) { }
Q_INVOKABLE QObject* from(int sourceInput);
@ -39,19 +40,11 @@ public:
Q_INVOKABLE QObject* enable(bool enable = true);
Q_INVOKABLE QObject* disable() { return enable(false); }
// JSON route creation point
Q_INVOKABLE QObject* from(const QJsonValue& json);
void parse(const QJsonObject& json);
// void serialize(QJsonObject& json);
protected:
QObject* from(const Endpoint::Pointer& source);
friend class RouteBuilderProxy;
ScriptingInterface& _parent;
UserInputMapper& _parent;
Mapping::Pointer _mapping;

View file

@ -22,7 +22,7 @@ namespace controller {
void RouteBuilderProxy::to(int destinationInput) {
qCDebug(controllers) << "Completing route " << destinationInput;
auto destinationEndpoint = _parent.endpointFor(UserInputMapper::Input(destinationInput));
auto destinationEndpoint = _parent.endpointFor(Input(destinationInput));
return to(destinationEndpoint);
}
@ -39,9 +39,9 @@ void RouteBuilderProxy::to(const QScriptValue& destination) {
}
void RouteBuilderProxy::to(const Endpoint::Pointer& destination) {
auto sourceEndpoint = _route->_source;
_route->_destination = destination;
_mapping->_channelMappings[sourceEndpoint].push_back(_route);
auto sourceEndpoint = _route->source;
_route->destination = destination;
_mapping->channelMappings[sourceEndpoint].push_back(_route);
deleteLater();
}
@ -104,37 +104,7 @@ void RouteBuilderProxy::addFilter(Filter::Lambda lambda) {
}
void RouteBuilderProxy::addFilter(Filter::Pointer filter) {
_route->_filters.push_back(filter);
}
QObject* RouteBuilderProxy::filters(const QJsonValue& json) {
// We expect an array of objects to define the filters
if (json.isArray()) {
const auto& jsonFilters = json.toArray();
for (auto jsonFilter : jsonFilters) {
if (jsonFilter.isObject()) {
// The filter is an object, now let s check for type and potential arguments
Filter::Pointer filter = Filter::parse(jsonFilter.toObject());
if (filter) {
addFilter(filter);
}
}
}
}
return this;
}
void RouteBuilderProxy::to(const QJsonValue& json) {
if (json.isString()) {
return to(_parent.endpointFor(_parent.inputFor(json.toString())));
} else if (json.isObject()) {
// Endpoint is defined as an object, we expect a js function then
//return to((Endpoint*) nullptr);
}
_route->filters.push_back(filter);
}
}

View file

@ -10,10 +10,11 @@
#define hifi_Controllers_Impl_RouteBuilderProxy_h
#include <QtCore/QObject>
#include "../Filter.h"
#include "../Route.h"
#include "../Mapping.h"
#include "../UserInputMapper.h"
class QJSValue;
class QScriptValue;
class QJsonValue;
@ -27,7 +28,7 @@ class ScriptingInterface;
class RouteBuilderProxy : public QObject {
Q_OBJECT
public:
RouteBuilderProxy(ScriptingInterface& parent, Mapping::Pointer mapping, Route::Pointer route)
RouteBuilderProxy(UserInputMapper& parent, Mapping::Pointer mapping, Route::Pointer route)
: _parent(parent), _mapping(mapping), _route(route) { }
Q_INVOKABLE void to(int destination);
@ -44,15 +45,11 @@ class RouteBuilderProxy : public QObject {
Q_INVOKABLE QObject* constrainToInteger();
Q_INVOKABLE QObject* constrainToPositiveInteger();
// JSON route creation point
Q_INVOKABLE QObject* filters(const QJsonValue& json);
Q_INVOKABLE void to(const QJsonValue& json);
private:
private:
void to(const Endpoint::Pointer& destination);
void addFilter(Filter::Lambda lambda);
void addFilter(Filter::Pointer filter);
ScriptingInterface& _parent;
UserInputMapper& _parent;
Mapping::Pointer _mapping;
Route::Pointer _route;
};

View file

@ -1,7 +1,7 @@
set(TARGET_NAME entities-renderer)
AUTOSCRIBE_SHADER_LIB(gpu model render render-utils)
setup_hifi_library(Widgets Network Script)
link_hifi_libraries(shared gpu procedural model model-networking script-engine controllers render render-utils)
link_hifi_libraries(shared gpu procedural model model-networking script-engine render render-utils)
target_bullet()

View file

@ -11,16 +11,15 @@
#include "Joystick.h"
#include <limits>
#include <glm/glm.hpp>
#include <PathUtils.h>
const float CONTROLLER_THRESHOLD = 0.3f;
#ifdef HAVE_SDL2
const float MAX_AXIS = 32768.0f;
Joystick::Joystick(SDL_JoystickID instanceId, const QString& name, SDL_GameController* sdlGameController) :
InputDevice(name),
Joystick::Joystick(SDL_JoystickID instanceId, SDL_GameController* sdlGameController) :
InputDevice("GamePad"),
_sdlGameController(sdlGameController),
_sdlJoystick(SDL_GameControllerGetJoystick(_sdlGameController)),
_instanceId(instanceId)
@ -72,136 +71,63 @@ void Joystick::handleButtonEvent(const SDL_ControllerButtonEvent& event) {
#endif
void Joystick::buildDeviceProxy(controller::DeviceProxy::Pointer proxy) {
using namespace controller;
proxy->_name = _name;
proxy->getButton = [this](const Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); };
proxy->getAxis = [this](const Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); };
proxy->getAvailabeInputs = [this]() -> QVector<Input::NamedPair> {
QVector<Input::NamedPair> availableInputs{
makePair(A, "A"),
makePair(B, "B"),
makePair(X, "X"),
makePair(Y, "Y"),
// DPad
makePair(DU, "DU"),
makePair(DD, "DD"),
makePair(DL, "DL"),
makePair(DR, "DR"),
// Bumpers
makePair(LB, "LB"),
makePair(RB, "RB"),
// Stick press
makePair(LS, "LS"),
makePair(RS, "RS"),
// Center buttons
makePair(START, "Start"),
makePair(BACK, "Back"),
// Analog sticks
makePair(LX, "LX"),
makePair(LY, "LY"),
makePair(RX, "RX"),
makePair(RY, "RY"),
// Triggers
makePair(LT, "LT"),
makePair(RT, "RT"),
void Joystick::registerToUserInputMapper(UserInputMapper& mapper) {
// Grab the current free device ID
_deviceID = mapper.getFreeDeviceID();
auto proxy = std::make_shared<UserInputMapper::DeviceProxy>(_name);
proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); };
proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); };
proxy->getAvailabeInputs = [this] () -> QVector<UserInputMapper::InputPair> {
QVector<UserInputMapper::InputPair> availableInputs;
// Buttons
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::A), "A"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::B), "B"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::X), "X"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::Y), "Y"));
// DPad
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DU), "DU"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DD), "DD"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DL), "DL"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DR), "DR"));
// Bumpers
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LB), "LB"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RB), "RB"));
// Stick press
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LS), "LS"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RS), "RS"));
// Center buttons
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::START), "Start"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::BACK), "Back"));
// Analog sticks
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LY), "LY"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LX), "LX"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RY), "RY"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RX), "RX"));
// Triggers
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LT), "LT"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RT), "RT"));
// Aliases, PlayStation style names
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LB), "L1"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RB), "R1"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LT), "L2"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RT), "R2"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LS), "L3"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RS), "R3"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::BACK), "Select"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::A), "Cross"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::B), "Circle"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::X), "Square"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::Y), "Triangle"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DU), "Up"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DD), "Down"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DL), "Left"));
availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DR), "Right"));
// Aliases, PlayStation style names
makePair(LB, "L1"),
makePair(RB, "R1"),
makePair(LT, "L2"),
makePair(RT, "R2"),
makePair(LS, "L3"),
makePair(RS, "R3"),
makePair(BACK, "Select"),
makePair(A, "Cross"),
makePair(B, "Circle"),
makePair(X, "Square"),
makePair(Y, "Triangle"),
makePair(DU, "Up"),
makePair(DD, "Down"),
makePair(DL, "Left"),
makePair(DR, "Right"),
};
return availableInputs;
};
proxy->resetDeviceBindings = [this, &mapper] () -> bool {
mapper.removeAllInputChannelsForDevice(_deviceID);
this->assignDefaultInputMapping(mapper);
return true;
};
mapper.registerDevice(_deviceID, proxy);
}
void Joystick::assignDefaultInputMapping(UserInputMapper& mapper) {
#if 0
#ifdef HAVE_SDL2
const float JOYSTICK_MOVE_SPEED = 1.0f;
const float DPAD_MOVE_SPEED = 0.5f;
const float JOYSTICK_YAW_SPEED = 0.5f;
const float JOYSTICK_PITCH_SPEED = 0.25f;
const float BOOM_SPEED = 0.1f;
// Y axes are flipped (up is negative)
// Left Joystick: Movement, strafing
mapper.addInputChannel(UserInputMapper::TRANSLATE_Z, makeInput(controller::LY), JOYSTICK_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::TRANSLATE_X, makeInput(controller::LX), JOYSTICK_MOVE_SPEED);
// Right Joystick: Camera orientation
mapper.addInputChannel(UserInputMapper::YAW, makeInput(controller::RX), JOYSTICK_YAW_SPEED);
mapper.addInputChannel(UserInputMapper::PITCH, makeInput(controller::RY), JOYSTICK_PITCH_SPEED);
// Dpad movement
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(controller::DU), DPAD_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(controller::DD), DPAD_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(controller::DR), DPAD_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(controller::DL), DPAD_MOVE_SPEED);
// Button controls
mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(controller::Y), DPAD_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(controller::X), DPAD_MOVE_SPEED);
// Zoom
mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(controller::RT), BOOM_SPEED);
mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(controller::LT), BOOM_SPEED);
// Hold front right shoulder button for precision controls
// Left Joystick: Movement, strafing
mapper.addInputChannel(UserInputMapper::TRANSLATE_Z, makeInput(controller::LY), makeInput(controller::RB), JOYSTICK_MOVE_SPEED / 2.0f);
mapper.addInputChannel(UserInputMapper::TRANSLATE_X, makeInput(controller::LY), makeInput(controller::RB), JOYSTICK_MOVE_SPEED / 2.0f);
// Right Joystick: Camera orientation
mapper.addInputChannel(UserInputMapper::YAW, makeInput(controller::RX), makeInput(controller::RB), JOYSTICK_YAW_SPEED / 2.0f);
mapper.addInputChannel(UserInputMapper::PITCH, makeInput(controller::RY), makeInput(controller::RB), JOYSTICK_PITCH_SPEED / 2.0f);
// Dpad movement
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(controller::DU), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f);
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(controller::DD), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f);
mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(controller::DR), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f);
mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(controller::DL), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f);
// Button controls
mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(controller::Y), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f);
mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(controller::X), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f);
// Zoom
mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(controller::RT), makeInput(controller::RB), BOOM_SPEED / 2.0f);
mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(controller::LT), makeInput(controller::RB), BOOM_SPEED / 2.0f);
mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(controller::RB));
mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(controller::B));
mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(controller::A));
#endif
#endif
}
QString Joystick::getDefaultMappingConfig() {
static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/xbox.json";
return MAPPING_JSON;
}

View file

@ -23,7 +23,7 @@
#include <controllers/InputDevice.h>
#include <controllers/StandardControls.h>
class Joystick : public QObject, public InputDevice {
class Joystick : public QObject, public controller::InputDevice {
Q_OBJECT
Q_PROPERTY(QString name READ getName)
@ -36,16 +36,16 @@ public:
const QString& getName() const { return _name; }
// Device functions
virtual void registerToUserInputMapper(UserInputMapper& mapper) override;
virtual void assignDefaultInputMapping(UserInputMapper& mapper) override;
virtual void buildDeviceProxy(controller::DeviceProxy::Pointer proxy) override;
virtual QString getDefaultMappingConfig() override;
virtual void update(float deltaTime, bool jointsCaptured) override;
virtual void focusOutEvent() override;
Joystick() : InputDevice("Joystick") {}
Joystick() : InputDevice("GamePad") {}
~Joystick();
#ifdef HAVE_SDL2
Joystick(SDL_JoystickID instanceId, const QString& name, SDL_GameController* sdlGameController);
Joystick(SDL_JoystickID instanceId, SDL_GameController* sdlGameController);
#endif
void closeJoystick();

View file

@ -14,6 +14,8 @@
#include <QtGui/QMouseEvent>
#include <QtGui/QTouchEvent>
#include <controllers/UserInputMapper.h>
#include <PathUtils.h>
const QString KeyboardMouseDevice::NAME = "Keyboard/Mouse";
@ -128,163 +130,153 @@ void KeyboardMouseDevice::touchUpdateEvent(const QTouchEvent* event) {
_lastTouch = currentPos;
}
UserInputMapper::Input KeyboardMouseDevice::makeInput(Qt::Key code) {
auto shortCode = (UserInputMapper::uint16)(code & KEYBOARD_MASK);
controller::Input KeyboardMouseDevice::makeInput(Qt::Key code) {
auto shortCode = (uint16_t)(code & KEYBOARD_MASK);
if (shortCode != code) {
shortCode |= 0x0800; // add this bit instead of the way Qt::Key add a bit on the 3rd byte for some keys
}
return UserInputMapper::Input(_deviceID, shortCode, UserInputMapper::ChannelType::BUTTON);
return controller::Input(_deviceID, shortCode, controller::ChannelType::BUTTON);
}
UserInputMapper::Input KeyboardMouseDevice::makeInput(Qt::MouseButton code) {
controller::Input KeyboardMouseDevice::makeInput(Qt::MouseButton code) {
switch (code) {
case Qt::LeftButton:
return UserInputMapper::Input(_deviceID, MOUSE_BUTTON_LEFT, UserInputMapper::ChannelType::BUTTON);
return controller::Input(_deviceID, MOUSE_BUTTON_LEFT, controller::ChannelType::BUTTON);
case Qt::RightButton:
return UserInputMapper::Input(_deviceID, MOUSE_BUTTON_RIGHT, UserInputMapper::ChannelType::BUTTON);
return controller::Input(_deviceID, MOUSE_BUTTON_RIGHT, controller::ChannelType::BUTTON);
case Qt::MiddleButton:
return UserInputMapper::Input(_deviceID, MOUSE_BUTTON_MIDDLE, UserInputMapper::ChannelType::BUTTON);
return controller::Input(_deviceID, MOUSE_BUTTON_MIDDLE, controller::ChannelType::BUTTON);
default:
return UserInputMapper::Input();
return controller::Input();
};
}
UserInputMapper::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::MouseAxisChannel axis) {
return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS);
controller::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::MouseAxisChannel axis) {
return controller::Input(_deviceID, axis, controller::ChannelType::AXIS);
}
UserInputMapper::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::TouchAxisChannel axis) {
return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS);
controller::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::TouchAxisChannel axis) {
return controller::Input(_deviceID, axis, controller::ChannelType::AXIS);
}
UserInputMapper::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::TouchButtonChannel button) {
return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON);
controller::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::TouchButtonChannel button) {
return controller::Input(_deviceID, button, controller::ChannelType::BUTTON);
}
void KeyboardMouseDevice::registerToUserInputMapper(UserInputMapper& mapper) {
// Grab the current free device ID
_deviceID = mapper.getFreeDeviceID();
auto proxy = std::make_shared<UserInputMapper::DeviceProxy>(_name);
proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); };
proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); };
proxy->getAvailabeInputs = [this] () -> QVector<UserInputMapper::InputPair> {
QVector<UserInputMapper::InputPair> availableInputs;
void KeyboardMouseDevice::buildDeviceProxy(controller::DeviceProxy::Pointer proxy) {
using namespace controller;
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<Input::NamedPair> {
QVector<Input::NamedPair> availableInputs;
for (int i = (int) Qt::Key_0; i <= (int) Qt::Key_9; i++) {
availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString()));
availableInputs.append(Input::NamedPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString()));
}
for (int i = (int) Qt::Key_A; i <= (int) Qt::Key_Z; i++) {
availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString()));
availableInputs.append(Input::NamedPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString()));
}
for (int i = (int) Qt::Key_Left; i <= (int) Qt::Key_Down; i++) {
availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString()));
availableInputs.append(Input::NamedPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString()));
}
availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key_Space), QKeySequence(Qt::Key_Space).toString()));
availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key_Shift), "Shift"));
availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key_PageUp), QKeySequence(Qt::Key_PageUp).toString()));
availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key_PageDown), QKeySequence(Qt::Key_PageDown).toString()));
availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Space), QKeySequence(Qt::Key_Space).toString()));
availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Shift), "Shift"));
availableInputs.append(Input::NamedPair(makeInput(Qt::Key_PageUp), QKeySequence(Qt::Key_PageUp).toString()));
availableInputs.append(Input::NamedPair(makeInput(Qt::Key_PageDown), QKeySequence(Qt::Key_PageDown).toString()));
availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::LeftButton), "Left Mouse Click"));
availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::MiddleButton), "Middle Mouse Click"));
availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::RightButton), "Right Mouse Click"));
availableInputs.append(Input::NamedPair(makeInput(Qt::LeftButton), "Left Mouse Click"));
availableInputs.append(Input::NamedPair(makeInput(Qt::MiddleButton), "Middle Mouse Click"));
availableInputs.append(Input::NamedPair(makeInput(Qt::RightButton), "Right Mouse Click"));
availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_X_POS), "Mouse Move Right"));
availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_X_NEG), "Mouse Move Left"));
availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_Y_POS), "Mouse Move Up"));
availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_Y_NEG), "Mouse Move Down"));
availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_X_POS), "Mouse Move Right"));
availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_X_NEG), "Mouse Move Left"));
availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_Y_POS), "Mouse Move Up"));
availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_Y_NEG), "Mouse Move Down"));
availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_WHEEL_Y_POS), "Mouse Wheel Right"));
availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_WHEEL_Y_NEG), "Mouse Wheel Left"));
availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_WHEEL_X_POS), "Mouse Wheel Up"));
availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_WHEEL_X_NEG), "Mouse Wheel Down"));
availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_WHEEL_Y_POS), "Mouse Wheel Right"));
availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_WHEEL_Y_NEG), "Mouse Wheel Left"));
availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_WHEEL_X_POS), "Mouse Wheel Up"));
availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_WHEEL_X_NEG), "Mouse Wheel Down"));
availableInputs.append(UserInputMapper::InputPair(makeInput(TOUCH_AXIS_X_POS), "Touchpad Right"));
availableInputs.append(UserInputMapper::InputPair(makeInput(TOUCH_AXIS_X_NEG), "Touchpad Left"));
availableInputs.append(UserInputMapper::InputPair(makeInput(TOUCH_AXIS_Y_POS), "Touchpad Up"));
availableInputs.append(UserInputMapper::InputPair(makeInput(TOUCH_AXIS_Y_NEG), "Touchpad Down"));
availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_X_POS), "Touchpad Right"));
availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_X_NEG), "Touchpad Left"));
availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_Y_POS), "Touchpad Up"));
availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_Y_NEG), "Touchpad Down"));
return availableInputs;
};
proxy->resetDeviceBindings = [this, &mapper] () -> bool {
mapper.removeAllInputChannelsForDevice(_deviceID);
this->assignDefaultInputMapping(mapper);
return true;
};
mapper.registerDevice(_deviceID, proxy);
}
void KeyboardMouseDevice::assignDefaultInputMapping(UserInputMapper& mapper) {
const float BUTTON_MOVE_SPEED = 1.0f;
const float BUTTON_YAW_SPEED = 0.75f;
const float BUTTON_PITCH_SPEED = 0.5f;
const float MOUSE_YAW_SPEED = 0.5f;
const float MOUSE_PITCH_SPEED = 0.25f;
const float TOUCH_YAW_SPEED = 0.5f;
const float TOUCH_PITCH_SPEED = 0.25f;
const float BUTTON_BOOM_SPEED = 0.1f;
// AWSD keys mapping
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(Qt::Key_S), BUTTON_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(Qt::Key_W), BUTTON_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(Qt::Key_A), BUTTON_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(Qt::Key_D), BUTTON_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(Qt::Key_C), BUTTON_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(Qt::Key_E), BUTTON_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(Qt::Key_E), makeInput(Qt::Key_Shift), BUTTON_BOOM_SPEED);
mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(Qt::Key_C), makeInput(Qt::Key_Shift), BUTTON_BOOM_SPEED);
mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(Qt::Key_A), makeInput(Qt::RightButton), BUTTON_YAW_SPEED);
mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(Qt::Key_D), makeInput(Qt::RightButton), BUTTON_YAW_SPEED);
mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(Qt::Key_A), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED);
mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(Qt::Key_D), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED);
mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(Qt::Key_S), makeInput(Qt::Key_Shift), BUTTON_PITCH_SPEED);
mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(Qt::Key_W), makeInput(Qt::Key_Shift), BUTTON_PITCH_SPEED);
// Arrow keys mapping
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(Qt::Key_Down), BUTTON_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(Qt::Key_Up), BUTTON_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(Qt::Key_Left), BUTTON_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(Qt::Key_Right), BUTTON_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(Qt::Key_PageDown), BUTTON_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(Qt::Key_PageUp), BUTTON_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(Qt::Key_Left), makeInput(Qt::RightButton), BUTTON_YAW_SPEED);
mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(Qt::Key_Right), makeInput(Qt::RightButton), BUTTON_YAW_SPEED);
mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(Qt::Key_Left), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED);
mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(Qt::Key_Right), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED);
mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(Qt::Key_Down), makeInput(Qt::Key_Shift), BUTTON_PITCH_SPEED);
mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(Qt::Key_Up), makeInput(Qt::Key_Shift), BUTTON_PITCH_SPEED);
// Mouse move
mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(MOUSE_AXIS_Y_NEG), makeInput(Qt::RightButton), MOUSE_PITCH_SPEED);
mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(MOUSE_AXIS_Y_POS), makeInput(Qt::RightButton), MOUSE_PITCH_SPEED);
mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(MOUSE_AXIS_X_NEG), makeInput(Qt::RightButton), MOUSE_YAW_SPEED);
mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(MOUSE_AXIS_X_POS), makeInput(Qt::RightButton), MOUSE_YAW_SPEED);
#ifdef Q_OS_MAC
// wheel event modifier on Mac collide with the touchpad scroll event
mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(TOUCH_AXIS_Y_NEG), TOUCH_PITCH_SPEED);
mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(TOUCH_AXIS_Y_POS), TOUCH_PITCH_SPEED);
mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(TOUCH_AXIS_X_NEG), TOUCH_YAW_SPEED);
mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(TOUCH_AXIS_X_POS), TOUCH_YAW_SPEED);
#else
// Touch pad yaw pitch
mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(TOUCH_AXIS_Y_NEG), TOUCH_PITCH_SPEED);
mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(TOUCH_AXIS_Y_POS), TOUCH_PITCH_SPEED);
mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(TOUCH_AXIS_X_NEG), TOUCH_YAW_SPEED);
mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(TOUCH_AXIS_X_POS), TOUCH_YAW_SPEED);
// Wheel move
mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(MOUSE_AXIS_WHEEL_Y_POS), BUTTON_BOOM_SPEED);
mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(MOUSE_AXIS_WHEEL_Y_NEG), BUTTON_BOOM_SPEED);
mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(MOUSE_AXIS_WHEEL_X_NEG), BUTTON_YAW_SPEED);
mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(MOUSE_AXIS_WHEEL_X_POS), BUTTON_YAW_SPEED);
#endif
mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(Qt::Key_Space));
mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(Qt::Key_R));
mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(Qt::Key_T));
QString KeyboardMouseDevice::getDefaultMappingConfig() {
static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/keyboardMouse.json";
return MAPPING_JSON;
}
//void KeyboardMouseDevice::assignDefaultInputMapping(UserInputMapper& mapper) {
// const float BUTTON_MOVE_SPEED = 1.0f;
// const float BUTTON_YAW_SPEED = 0.75f;
// const float BUTTON_PITCH_SPEED = 0.5f;
// const float MOUSE_YAW_SPEED = 0.5f;
// const float MOUSE_PITCH_SPEED = 0.25f;
// const float TOUCH_YAW_SPEED = 0.5f;
// const float TOUCH_PITCH_SPEED = 0.25f;
// const float BUTTON_BOOM_SPEED = 0.1f;
//
// // AWSD keys mapping
//
// mapper.addInputChannel(controller::BOOM_IN, makeInput(Qt::Key_E), makeInput(Qt::Key_Shift), BUTTON_BOOM_SPEED);
// mapper.addInputChannel(controller::BOOM_OUT, makeInput(Qt::Key_C), makeInput(Qt::Key_Shift), BUTTON_BOOM_SPEED);
// mapper.addInputChannel(controller::LATERAL_LEFT, makeInput(Qt::Key_A), makeInput(Qt::RightButton), BUTTON_YAW_SPEED);
// mapper.addInputChannel(controller::LATERAL_RIGHT, makeInput(Qt::Key_D), makeInput(Qt::RightButton), BUTTON_YAW_SPEED);
// mapper.addInputChannel(controller::LATERAL_LEFT, makeInput(Qt::Key_A), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED);
// mapper.addInputChannel(controller::LATERAL_RIGHT, makeInput(Qt::Key_D), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED);
// mapper.addInputChannel(controller::PITCH_DOWN, makeInput(Qt::Key_S), makeInput(Qt::Key_Shift), BUTTON_PITCH_SPEED);
// mapper.addInputChannel(controller::PITCH_UP, makeInput(Qt::Key_W), makeInput(Qt::Key_Shift), BUTTON_PITCH_SPEED);
//
// // Arrow keys mapping
// mapper.addInputChannel(controller::LONGITUDINAL_BACKWARD, makeInput(Qt::Key_Down), BUTTON_MOVE_SPEED);
// mapper.addInputChannel(controller::LONGITUDINAL_FORWARD, makeInput(Qt::Key_Up), BUTTON_MOVE_SPEED);
// mapper.addInputChannel(controller::YAW_LEFT, makeInput(Qt::Key_Left), BUTTON_MOVE_SPEED);
// mapper.addInputChannel(controller::YAW_RIGHT, makeInput(Qt::Key_Right), BUTTON_MOVE_SPEED);
// mapper.addInputChannel(controller::VERTICAL_DOWN, makeInput(Qt::Key_PageDown), BUTTON_MOVE_SPEED);
// mapper.addInputChannel(controller::VERTICAL_UP, makeInput(Qt::Key_PageUp), BUTTON_MOVE_SPEED);
//
// mapper.addInputChannel(controller::LATERAL_LEFT, makeInput(Qt::Key_Left), makeInput(Qt::RightButton), BUTTON_YAW_SPEED);
// mapper.addInputChannel(controller::LATERAL_RIGHT, makeInput(Qt::Key_Right), makeInput(Qt::RightButton), BUTTON_YAW_SPEED);
// mapper.addInputChannel(controller::LATERAL_LEFT, makeInput(Qt::Key_Left), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED);
// mapper.addInputChannel(controller::LATERAL_RIGHT, makeInput(Qt::Key_Right), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED);
// mapper.addInputChannel(controller::PITCH_DOWN, makeInput(Qt::Key_Down), makeInput(Qt::Key_Shift), BUTTON_PITCH_SPEED);
// mapper.addInputChannel(controller::PITCH_UP, makeInput(Qt::Key_Up), makeInput(Qt::Key_Shift), BUTTON_PITCH_SPEED);
//
// // Mouse move
// mapper.addInputChannel(controller::PITCH_DOWN, makeInput(MOUSE_AXIS_Y_NEG), makeInput(Qt::RightButton), MOUSE_PITCH_SPEED);
// mapper.addInputChannel(controller::PITCH_UP, makeInput(MOUSE_AXIS_Y_POS), makeInput(Qt::RightButton), MOUSE_PITCH_SPEED);
// mapper.addInputChannel(controller::YAW_LEFT, makeInput(MOUSE_AXIS_X_NEG), makeInput(Qt::RightButton), MOUSE_YAW_SPEED);
// mapper.addInputChannel(controller::YAW_RIGHT, makeInput(MOUSE_AXIS_X_POS), makeInput(Qt::RightButton), MOUSE_YAW_SPEED);
//
//
//#ifdef Q_OS_MAC
// // wheel event modifier on Mac collide with the touchpad scroll event
// mapper.addInputChannel(controller::PITCH_DOWN, makeInput(TOUCH_AXIS_Y_NEG), TOUCH_PITCH_SPEED);
// mapper.addInputChannel(controller::PITCH_UP, makeInput(TOUCH_AXIS_Y_POS), TOUCH_PITCH_SPEED);
// mapper.addInputChannel(controller::YAW_LEFT, makeInput(TOUCH_AXIS_X_NEG), TOUCH_YAW_SPEED);
// mapper.addInputChannel(controller::YAW_RIGHT, makeInput(TOUCH_AXIS_X_POS), TOUCH_YAW_SPEED);
//#else
// // Touch pad yaw pitch
// mapper.addInputChannel(controller::PITCH_DOWN, makeInput(TOUCH_AXIS_Y_NEG), TOUCH_PITCH_SPEED);
// mapper.addInputChannel(controller::PITCH_UP, makeInput(TOUCH_AXIS_Y_POS), TOUCH_PITCH_SPEED);
// mapper.addInputChannel(controller::YAW_LEFT, makeInput(TOUCH_AXIS_X_NEG), TOUCH_YAW_SPEED);
// mapper.addInputChannel(controller::YAW_RIGHT, makeInput(TOUCH_AXIS_X_POS), TOUCH_YAW_SPEED);
//
// // Wheel move
// mapper.addInputChannel(controller::BOOM_IN, makeInput(MOUSE_AXIS_WHEEL_Y_POS), BUTTON_BOOM_SPEED);
// mapper.addInputChannel(controller::BOOM_OUT, makeInput(MOUSE_AXIS_WHEEL_Y_NEG), BUTTON_BOOM_SPEED);
// mapper.addInputChannel(controller::LATERAL_LEFT, makeInput(MOUSE_AXIS_WHEEL_X_NEG), BUTTON_YAW_SPEED);
// mapper.addInputChannel(controller::LATERAL_RIGHT, makeInput(MOUSE_AXIS_WHEEL_X_POS), BUTTON_YAW_SPEED);
//
//#endif
//
// mapper.addInputChannel(controller::SHIFT, makeInput(Qt::Key_Space));
// mapper.addInputChannel(controller::ACTION1, makeInput(Qt::Key_R));
// mapper.addInputChannel(controller::ACTION2, makeInput(Qt::Key_T));
//}

View file

@ -24,7 +24,7 @@ class QKeyEvent;
class QMouseEvent;
class QWheelEvent;
class KeyboardMouseDevice : public InputPlugin, public InputDevice {
class KeyboardMouseDevice : public InputPlugin, public controller::InputDevice {
Q_OBJECT
public:
enum KeyboardChannel {
@ -72,8 +72,8 @@ public:
virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override { update(deltaTime, jointsCaptured); }
// Device functions
virtual void registerToUserInputMapper(UserInputMapper& mapper) override;
virtual void assignDefaultInputMapping(UserInputMapper& mapper) override;
virtual void buildDeviceProxy(controller::DeviceProxy::Pointer proxy) override;
virtual QString getDefaultMappingConfig() override;
virtual void update(float deltaTime, bool jointsCaptured) override;
virtual void focusOutEvent() override;
@ -91,11 +91,11 @@ public:
void wheelEvent(QWheelEvent* event);
// Let's make it easy for Qt because we assume we love Qt forever
UserInputMapper::Input makeInput(Qt::Key code);
UserInputMapper::Input makeInput(Qt::MouseButton code);
UserInputMapper::Input makeInput(KeyboardMouseDevice::MouseAxisChannel axis);
UserInputMapper::Input makeInput(KeyboardMouseDevice::TouchAxisChannel axis);
UserInputMapper::Input makeInput(KeyboardMouseDevice::TouchButtonChannel button);
controller::Input makeInput(Qt::Key code);
controller::Input makeInput(Qt::MouseButton code);
controller::Input makeInput(MouseAxisChannel axis);
controller::Input makeInput(TouchAxisChannel axis);
controller::Input makeInput(TouchButtonChannel button);
static const QString NAME;

View file

@ -14,6 +14,7 @@
#include <HFActionEvent.h>
#include <HFBackEvent.h>
#include <PerfStat.h>
#include <controllers/UserInputMapper.h>
#include "SDL2Manager.h"
@ -47,11 +48,11 @@ void SDL2Manager::init() {
if (controller) {
SDL_JoystickID id = getInstanceId(controller);
if (!_openJoysticks.contains(id)) {
Joystick* joystick = new Joystick(id, SDL_GameControllerName(controller), controller);
//Joystick* joystick = new Joystick(id, SDL_GameControllerName(controller), controller);
Joystick* joystick = new Joystick(id, controller);
_openJoysticks[id] = joystick;
auto userInputMapper = DependencyManager::get<UserInputMapper>();
joystick->registerToUserInputMapper(*userInputMapper);
joystick->assignDefaultInputMapping(*userInputMapper);
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
userInputMapper->registerDevice(joystick);
emit joystickAdded(joystick);
}
}
@ -92,7 +93,7 @@ void SDL2Manager::pluginFocusOutEvent() {
void SDL2Manager::pluginUpdate(float deltaTime, bool jointsCaptured) {
#ifdef HAVE_SDL2
if (_isInitialized) {
auto userInputMapper = DependencyManager::get<UserInputMapper>();
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
for (auto joystick : _openJoysticks) {
joystick->update(deltaTime, jointsCaptured);
}
@ -124,13 +125,11 @@ void SDL2Manager::pluginUpdate(float deltaTime, bool jointsCaptured) {
} else if (event.type == SDL_CONTROLLERDEVICEADDED) {
SDL_GameController* controller = SDL_GameControllerOpen(event.cdevice.which);
SDL_JoystickID id = getInstanceId(controller);
if (!_openJoysticks.contains(id)) {
Joystick* joystick = new Joystick(id, SDL_GameControllerName(controller), controller);
_openJoysticks[id] = joystick;
joystick->registerToUserInputMapper(*userInputMapper);
joystick->assignDefaultInputMapping(*userInputMapper);
// Joystick* joystick = new Joystick(id, SDL_GameControllerName(controller), controller);
Joystick* joystick = new Joystick(id, controller);
userInputMapper->registerDevice(joystick);
emit joystickAdded(joystick);
}
} else if (event.type == SDL_CONTROLLERDEVICEREMOVED) {

View file

@ -18,10 +18,13 @@
#include <PerfStat.h>
#include <SettingHandle.h>
#include <plugins/PluginContainer.h>
#include <PathUtils.h>
#include <NumericalConstants.h>
#include <UserActivityLogger.h>
#include <controllers/UserInputMapper.h>
#include "NumericalConstants.h"
#include "SixenseManager.h"
#include "UserActivityLogger.h"
#ifdef HAVE_SIXENSE
#include "sixense.h"
@ -119,8 +122,8 @@ void SixenseManager::activate() {
loadSettings();
sixenseInit();
_activated = true;
auto userInputMapper = DependencyManager::get<UserInputMapper>();
registerToUserInputMapper(*userInputMapper);
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
userInputMapper->registerDevice(this);
#endif
}
@ -171,7 +174,7 @@ void SixenseManager::update(float deltaTime, bool jointsCaptured) {
(SixenseBaseFunction) _sixenseLibrary->resolve("sixenseGetNumActiveControllers");
#endif
auto userInputMapper = DependencyManager::get<UserInputMapper>();
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
if (sixenseGetNumActiveControllers() == 0) {
_poseStateMap.clear();
@ -232,7 +235,7 @@ void SixenseManager::update(float deltaTime, bool jointsCaptured) {
_poseStateMap.clear();
}
} else {
_poseStateMap[left ? controller::StandardPoseChannel::LEFT : controller::StandardPoseChannel::RIGHT] = UserInputMapper::PoseValue();
_poseStateMap[left ? controller::StandardPoseChannel::LEFT : controller::StandardPoseChannel::RIGHT] = controller::Pose();
}
}
@ -440,8 +443,7 @@ void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, boo
// TODO: find a shortcut with fewer rotations.
rotation = _avatarRotation * postOffset * glm::inverse(sixenseToHand) * rotation * preOffset * sixenseToHand;
_poseStateMap[left ? controller::StandardPoseChannel::LEFT : controller::StandardPoseChannel::RIGHT] =
UserInputMapper::PoseValue(position, rotation);
_poseStateMap[left ? controller::StandardPoseChannel::LEFT : controller::StandardPoseChannel::RIGHT] = controller::Pose(position, rotation);
#endif // HAVE_SIXENSE
}
@ -456,85 +458,91 @@ static const auto R2 = controller::A;
static const auto R3 = controller::B;
static const auto R4 = controller::Y;
void SixenseManager::registerToUserInputMapper(UserInputMapper& mapper) {
// Grab the current free device ID
_deviceID = mapper.getFreeDeviceID();
auto proxy = std::make_shared<UserInputMapper::DeviceProxy>(_name);
proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); };
proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); };
proxy->getPose = [this](const UserInputMapper::Input& input, int timestamp) -> UserInputMapper::PoseValue { return this->getPose(input.getChannel()); };
using namespace controller;
proxy->getAvailabeInputs = [this]() -> QVector<UserInputMapper::InputPair> {
QVector<UserInputMapper::InputPair> availableInputs;
availableInputs.append(UserInputMapper::InputPair(makeInput(L0), "L0"));
availableInputs.append(UserInputMapper::InputPair(makeInput(L1), "L1"));
availableInputs.append(UserInputMapper::InputPair(makeInput(L2), "L2"));
availableInputs.append(UserInputMapper::InputPair(makeInput(L3), "L3"));
availableInputs.append(UserInputMapper::InputPair(makeInput(L4), "L4"));
availableInputs.append(UserInputMapper::InputPair(makeInput(LB), "LB"));
availableInputs.append(UserInputMapper::InputPair(makeInput(LS), "LS"));
availableInputs.append(UserInputMapper::InputPair(makeInput(LX), "LX"));
availableInputs.append(UserInputMapper::InputPair(makeInput(LY), "LY"));
availableInputs.append(UserInputMapper::InputPair(makeInput(LT), "LT"));
availableInputs.append(UserInputMapper::InputPair(makeInput(R0), "R0"));
availableInputs.append(UserInputMapper::InputPair(makeInput(R1), "R1"));
availableInputs.append(UserInputMapper::InputPair(makeInput(R2), "R2"));
availableInputs.append(UserInputMapper::InputPair(makeInput(R3), "R3"));
availableInputs.append(UserInputMapper::InputPair(makeInput(R4), "R4"));
availableInputs.append(UserInputMapper::InputPair(makeInput(RB), "RB"));
availableInputs.append(UserInputMapper::InputPair(makeInput(RS), "RS"));
availableInputs.append(UserInputMapper::InputPair(makeInput(RX), "RX"));
availableInputs.append(UserInputMapper::InputPair(makeInput(RY), "RY"));
availableInputs.append(UserInputMapper::InputPair(makeInput(RT), "RT"));
availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT), "LeftPose"));
availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT), "RightPose"));
using namespace controller;
void SixenseManager::buildDeviceProxy(controller::DeviceProxy::Pointer proxy) {
proxy->getButton = [this](const Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); };
proxy->getAxis = [this](const Input& input, int timestamp) -> float {
return this->getAxis(input.getChannel());
};
proxy->getPose = [this](const Input& input, int timestamp) -> Pose { return this->getPose(input.getChannel()); };
proxy->getAvailabeInputs = [this]() -> QVector<Input::NamedPair> {
QVector<Input::NamedPair> availableInputs;
availableInputs.append(Input::NamedPair(makeInput(L0), "L0"));
availableInputs.append(Input::NamedPair(makeInput(L1), "L1"));
availableInputs.append(Input::NamedPair(makeInput(L2), "L2"));
availableInputs.append(Input::NamedPair(makeInput(L3), "L3"));
availableInputs.append(Input::NamedPair(makeInput(L4), "L4"));
availableInputs.append(Input::NamedPair(makeInput(LB), "LB"));
availableInputs.append(Input::NamedPair(makeInput(LS), "LS"));
availableInputs.append(Input::NamedPair(makeInput(LX), "LX"));
availableInputs.append(Input::NamedPair(makeInput(LY), "LY"));
availableInputs.append(Input::NamedPair(makeInput(LT), "LT"));
availableInputs.append(Input::NamedPair(makeInput(R0), "R0"));
availableInputs.append(Input::NamedPair(makeInput(R1), "R1"));
availableInputs.append(Input::NamedPair(makeInput(R2), "R2"));
availableInputs.append(Input::NamedPair(makeInput(R3), "R3"));
availableInputs.append(Input::NamedPair(makeInput(R4), "R4"));
availableInputs.append(Input::NamedPair(makeInput(RB), "RB"));
availableInputs.append(Input::NamedPair(makeInput(RS), "RS"));
availableInputs.append(Input::NamedPair(makeInput(RX), "RX"));
availableInputs.append(Input::NamedPair(makeInput(RY), "RY"));
availableInputs.append(Input::NamedPair(makeInput(RT), "RT"));
availableInputs.append(Input::NamedPair(makeInput(LEFT), "LeftPose"));
availableInputs.append(Input::NamedPair(makeInput(RIGHT), "RightPose"));
return availableInputs;
};
mapper.registerDevice(_deviceID, proxy);
}
void SixenseManager::assignDefaultInputMapping(UserInputMapper& mapper) {
const float JOYSTICK_MOVE_SPEED = 1.0f;
const float JOYSTICK_YAW_SPEED = 0.5f;
const float JOYSTICK_PITCH_SPEED = 0.25f;
const float BUTTON_MOVE_SPEED = 1.0f;
const float BOOM_SPEED = 0.1f;
using namespace controller;
// Left Joystick: Movement, strafing
mapper.addInputChannel(UserInputMapper::TRANSLATE_Z, makeInput(LY), JOYSTICK_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::TRANSLATE_X, makeInput(LX), JOYSTICK_MOVE_SPEED);
// Right Joystick: Camera orientation
mapper.addInputChannel(UserInputMapper::YAW, makeInput(RX), JOYSTICK_YAW_SPEED);
mapper.addInputChannel(UserInputMapper::PITCH, makeInput(RY), JOYSTICK_PITCH_SPEED);
// Buttons
mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(L3), BOOM_SPEED);
mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(L1), BOOM_SPEED);
mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(R3), BUTTON_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(R1), BUTTON_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(L2));
mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(R2));
mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(L4));
mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(R4));
// FIXME
// mapper.addInputChannel(UserInputMapper::LEFT_HAND, makeInput(LEFT_HAND));
// mapper.addInputChannel(UserInputMapper::RIGHT_HAND, makeInput(RIGHT_HAND));
mapper.addInputChannel(UserInputMapper::LEFT_HAND_CLICK, makeInput(LT));
mapper.addInputChannel(UserInputMapper::RIGHT_HAND_CLICK, makeInput(RT));
// TODO find a mechanism to allow users to navigate the context menu via
mapper.addInputChannel(UserInputMapper::CONTEXT_MENU, makeInput(L0));
mapper.addInputChannel(UserInputMapper::TOGGLE_MUTE, makeInput(R0));
QString SixenseManager::getDefaultMappingConfig() {
static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/hydra.json";
return MAPPING_JSON;
}
//
//void SixenseManager::assignDefaultInputMapping(UserInputMapper& mapper) {
// const float JOYSTICK_MOVE_SPEED = 1.0f;
// const float JOYSTICK_YAW_SPEED = 0.5f;
// const float JOYSTICK_PITCH_SPEED = 0.25f;
// const float BUTTON_MOVE_SPEED = 1.0f;
// const float BOOM_SPEED = 0.1f;
// using namespace controller;
//
// // Left Joystick: Movement, strafing
// mapper.addInputChannel(UserInputMapper::TRANSLATE_Z, makeInput(LY), JOYSTICK_MOVE_SPEED);
// mapper.addInputChannel(UserInputMapper::TRANSLATE_X, makeInput(LX), JOYSTICK_MOVE_SPEED);
//
// // Right Joystick: Camera orientation
// mapper.addInputChannel(UserInputMapper::YAW, makeInput(RX), JOYSTICK_YAW_SPEED);
// mapper.addInputChannel(UserInputMapper::PITCH, makeInput(RY), JOYSTICK_PITCH_SPEED);
//
// // Buttons
// mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(L3), BOOM_SPEED);
// mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(L1), BOOM_SPEED);
//
// mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(R3), BUTTON_MOVE_SPEED);
// mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(R1), BUTTON_MOVE_SPEED);
//
// mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(L2));
// mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(R2));
//
// mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(L4));
// mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(R4));
//
// // FIXME
//// mapper.addInputChannel(UserInputMapper::LEFT_HAND, makeInput(LEFT_HAND));
//// mapper.addInputChannel(UserInputMapper::RIGHT_HAND, makeInput(RIGHT_HAND));
//
// mapper.addInputChannel(UserInputMapper::LEFT_HAND_CLICK, makeInput(LT));
// mapper.addInputChannel(UserInputMapper::RIGHT_HAND_CLICK, makeInput(RT));
//
// // TODO find a mechanism to allow users to navigate the context menu via
// mapper.addInputChannel(UserInputMapper::CONTEXT_MENU, makeInput(L0));
// mapper.addInputChannel(UserInputMapper::TOGGLE_MUTE, makeInput(R0));
//
//}
// virtual
void SixenseManager::saveSettings() const {
Settings settings;

View file

@ -42,7 +42,7 @@ const unsigned int BUTTON_TRIGGER = 1U << 8;
const bool DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS = false;
// Handles interaction with the Sixense SDK (e.g., Razer Hydra).
class SixenseManager : public InputPlugin, public InputDevice {
class SixenseManager : public InputPlugin, public controller::InputDevice {
Q_OBJECT
public:
SixenseManager();
@ -60,8 +60,8 @@ public:
virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override { update(deltaTime, jointsCaptured); }
// Device functions
virtual void registerToUserInputMapper(UserInputMapper& mapper) override;
virtual void assignDefaultInputMapping(UserInputMapper& mapper) override;
virtual void buildDeviceProxy(controller::DeviceProxy::Pointer proxy) override;
virtual QString getDefaultMappingConfig() override;
virtual void update(float deltaTime, bool jointsCaptured) override;
virtual void focusOutEvent() override;

View file

@ -12,15 +12,19 @@
#include "ViveControllerManager.h"
#include <PerfStat.h>
#include "GeometryCache.h"
#include <PathUtils.h>
#include <GeometryCache.h>
#include <gpu/Batch.h>
#include <gpu/Context.h>
#include <DeferredLightingEffect.h>
#include <display-plugins/openvr/OpenVrHelpers.h>
#include "NumericalConstants.h"
#include <NumericalConstants.h>
#include <plugins/PluginContainer.h>
#include "UserActivityLogger.h"
#include <UserActivityLogger.h>
#include <controllers/UserInputMapper.h>
#include <controllers/StandardControls.h>
#ifdef Q_OS_WIN
extern vr::IVRSystem* _hmd;
@ -29,18 +33,6 @@ extern vr::TrackedDevicePose_t _trackedDevicePose[vr::k_unMaxTrackedDeviceCount]
extern mat4 _trackedDevicePoseMat4[vr::k_unMaxTrackedDeviceCount];
#endif
const unsigned int LEFT_MASK = 0U;
const unsigned int RIGHT_MASK = 1U;
const uint64_t VR_BUTTON_A = 1U << 1;
const uint64_t VR_GRIP_BUTTON = 1U << 2;
const uint64_t VR_TRACKPAD_BUTTON = 1ULL << 32;
const uint64_t VR_TRIGGER_BUTTON = 1ULL << 33;
const unsigned int BUTTON_A = 1U << 1;
const unsigned int GRIP_BUTTON = 1U << 2;
const unsigned int TRACKPAD_BUTTON = 1U << 3;
const unsigned int TRIGGER_BUTTON = 1U << 4;
const float CONTROLLER_LENGTH_OFFSET = 0.0762f; // three inches
const QString CONTROLLER_MODEL_STRING = "vr_controller_05_wireless_b";
@ -173,8 +165,8 @@ void ViveControllerManager::updateRendering(RenderArgs* args, render::ScenePoint
//pendingChanges.updateItem(_leftHandRenderID, );
UserInputMapper::PoseValue leftHand = _poseStateMap[makeInput(JointChannel::LEFT_HAND).getChannel()];
UserInputMapper::PoseValue rightHand = _poseStateMap[makeInput(JointChannel::RIGHT_HAND).getChannel()];
controller::Pose leftHand = _poseStateMap[controller::StandardPoseChannel::LEFT];
controller::Pose rightHand = _poseStateMap[controller::StandardPoseChannel::RIGHT];
gpu::doInBatch(args->_context, [=](gpu::Batch& batch) {
auto geometryCache = DependencyManager::get<GeometryCache>();
@ -186,21 +178,20 @@ void ViveControllerManager::updateRendering(RenderArgs* args, render::ScenePoint
//batch._glBindTexture(GL_TEXTURE_2D, _uexture);
if (leftHand.isValid()) {
renderHand(leftHand, batch, LEFT_HAND);
renderHand(leftHand, batch, 1);
}
if (rightHand.isValid()) {
renderHand(rightHand, batch, RIGHT_HAND);
renderHand(rightHand, batch, -1);
}
});
}
}
void ViveControllerManager::renderHand(UserInputMapper::PoseValue pose, gpu::Batch& batch, int index) {
auto userInputMapper = DependencyManager::get<UserInputMapper>();
void ViveControllerManager::renderHand(const controller::Pose& pose, gpu::Batch& batch, int sign) {
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
Transform transform(userInputMapper->getSensorToWorldMat());
transform.postTranslate(pose.getTranslation() + pose.getRotation() * glm::vec3(0, 0, CONTROLLER_LENGTH_OFFSET));
int sign = index == LEFT_HAND ? 1 : -1;
glm::quat rotation = pose.getRotation() * glm::angleAxis(PI, glm::vec3(1.0f, 0.0f, 0.0f)) * glm::angleAxis(sign * PI_OVER_TWO, glm::vec3(0.0f, 0.0f, 1.0f));
transform.postRotate(rotation);
@ -251,6 +242,7 @@ void ViveControllerManager::update(float deltaTime, bool jointsCaptured) {
}
numTrackedControllers++;
bool left = numTrackedControllers == 1;
const mat4& mat = _trackedDevicePoseMat4[device];
@ -264,14 +256,18 @@ void ViveControllerManager::update(float deltaTime, bool jointsCaptured) {
//qDebug() << (numTrackedControllers == 1 ? "Left: " : "Right: ");
//qDebug() << "Trackpad: " << controllerState.rAxis[0].x << " " << controllerState.rAxis[0].y;
//qDebug() << "Trigger: " << controllerState.rAxis[1].x << " " << controllerState.rAxis[1].y;
handleButtonEvent(controllerState.ulButtonPressed, numTrackedControllers - 1);
for (int i = 0; i < vr::k_unControllerStateAxisCount; i++) {
handleAxisEvent(Axis(i), controllerState.rAxis[i].x, controllerState.rAxis[i].y, numTrackedControllers - 1);
for (uint32_t i = 0; i < vr::k_EButton_Max; ++i) {
auto mask = vr::ButtonMaskFromId((vr::EVRButtonId)i);
bool pressed = 0 != (controllerState.ulButtonPressed & mask);
handleButtonEvent(i, pressed, left);
}
for (uint32_t i = 0; i < vr::k_unControllerStateAxisCount; i++) {
handleAxisEvent(i, controllerState.rAxis[i].x, controllerState.rAxis[i].y, left);
}
}
}
auto userInputMapper = DependencyManager::get<UserInputMapper>();
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
if (numTrackedControllers == 0) {
if (_deviceID != 0) {
@ -282,8 +278,7 @@ void ViveControllerManager::update(float deltaTime, bool jointsCaptured) {
}
if (_trackedControllers == 0 && numTrackedControllers > 0) {
registerToUserInputMapper(*userInputMapper);
assignDefaultInputMapping(*userInputMapper);
userInputMapper->registerDevice(this);
UserActivityLogger::getInstance().connectedDevice("spatial_controller", "steamVR");
}
@ -296,33 +291,41 @@ void ViveControllerManager::focusOutEvent() {
_buttonPressedMap.clear();
};
void ViveControllerManager::handleAxisEvent(Axis axis, float x, float y, int index) {
if (axis == TRACKPAD_AXIS) {
_axisStateMap[makeInput(AXIS_Y_POS, index).getChannel()] = (y > 0.0f) ? y : 0.0f;
_axisStateMap[makeInput(AXIS_Y_NEG, index).getChannel()] = (y < 0.0f) ? -y : 0.0f;
_axisStateMap[makeInput(AXIS_X_POS, index).getChannel()] = (x > 0.0f) ? x : 0.0f;
_axisStateMap[makeInput(AXIS_X_NEG, index).getChannel()] = (x < 0.0f) ? -x : 0.0f;
} else if (axis == TRIGGER_AXIS) {
_axisStateMap[makeInput(BACK_TRIGGER, index).getChannel()] = x;
// These functions do translation from the Steam IDs to the standard controller IDs
void ViveControllerManager::handleAxisEvent(uint32_t axis, float x, float y, bool left) {
#ifdef Q_OS_WIN
using namespace controller;
if (axis == vr::k_EButton_SteamVR_Touchpad) {
_axisStateMap[left ? LX : RX] = x;
_axisStateMap[left ? LY : RY] = y;
} else if (axis == vr::k_EButton_SteamVR_Trigger) {
_axisStateMap[left ? LT : RT] = x;
}
#endif
}
void ViveControllerManager::handleButtonEvent(uint64_t buttons, int index) {
if (buttons & VR_BUTTON_A) {
_buttonPressedMap.insert(makeInput(BUTTON_A, index).getChannel());
// These functions do translation from the Steam IDs to the standard controller IDs
void ViveControllerManager::handleButtonEvent(uint32_t button, bool pressed, bool left) {
#ifdef Q_OS_WIN
if (!pressed) {
return;
}
if (buttons & VR_GRIP_BUTTON) {
_buttonPressedMap.insert(makeInput(GRIP_BUTTON, index).getChannel());
}
if (buttons & VR_TRACKPAD_BUTTON) {
_buttonPressedMap.insert(makeInput(TRACKPAD_BUTTON, index).getChannel());
}
if (buttons & VR_TRIGGER_BUTTON) {
_buttonPressedMap.insert(makeInput(TRIGGER_BUTTON, index).getChannel());
if (button == vr::k_EButton_ApplicationMenu) {
// FIXME?
_buttonPressedMap.insert(left ? controller::A : controller::A);
} else if (button == vr::k_EButton_Grip) {
// Tony says these are harder to reach, so make them the meta buttons
_buttonPressedMap.insert(left ? controller::BACK : controller::START);
} else if (button == vr::k_EButton_SteamVR_Trigger) {
_buttonPressedMap.insert(left ? controller::LB : controller::RB);
} else if (button == vr::k_EButton_SteamVR_Touchpad) {
_buttonPressedMap.insert(left ? controller::LS : controller::RS);
}
#endif
}
void ViveControllerManager::handlePoseEvent(const mat4& mat, int index) {
void ViveControllerManager::handlePoseEvent(const mat4& mat, bool left) {
glm::vec3 position = extractTranslation(mat);
glm::quat rotation = glm::quat_cast(mat);
@ -378,7 +381,7 @@ void ViveControllerManager::handlePoseEvent(const mat4& mat, int index) {
const glm::quat quarterX = glm::angleAxis(PI / 2.0f, glm::vec3(1.0f, 0.0f, 0.0f));
const glm::quat yFlip = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
float sign = (index == LEFT_HAND) ? -1.0f : 1.0f;
float sign = left ? -1.0f : 1.0f;
const glm::quat signedQuaterZ = glm::angleAxis(sign * PI / 2.0f, glm::vec3(0.0f, 0.0f, 1.0f));
const glm::quat eighthX = glm::angleAxis(PI / 4.0f, glm::vec3(1.0f, 0.0f, 0.0f));
const glm::quat offset = glm::inverse(signedQuaterZ * eighthX);
@ -386,92 +389,93 @@ void ViveControllerManager::handlePoseEvent(const mat4& mat, int index) {
position += rotation * glm::vec3(0, 0, -CONTROLLER_LENGTH_OFFSET);
_poseStateMap[makeInput(JointChannel(index)).getChannel()] = UserInputMapper::PoseValue(position, rotation);
_poseStateMap[left ? controller::LEFT : controller::RIGHT] = controller::Pose(position, rotation);
}
void ViveControllerManager::registerToUserInputMapper(UserInputMapper& mapper) {
// Grab the current free device ID
_deviceID = mapper.getFreeDeviceID();
auto proxy = UserInputMapper::DeviceProxy::Pointer(new UserInputMapper::DeviceProxy(_name));
proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); };
proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); };
proxy->getPose = [this](const UserInputMapper::Input& input, int timestamp) -> UserInputMapper::PoseValue { return this->getPose(input.getChannel()); };
proxy->getAvailabeInputs = [this] () -> QVector<UserInputMapper::InputPair> {
QVector<UserInputMapper::InputPair> availableInputs;
availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_HAND), "Left Hand"));
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_A, 0), "Left Button A"));
availableInputs.append(UserInputMapper::InputPair(makeInput(GRIP_BUTTON, 0), "Left Grip Button"));
availableInputs.append(UserInputMapper::InputPair(makeInput(TRACKPAD_BUTTON, 0), "Left Trackpad Button"));
availableInputs.append(UserInputMapper::InputPair(makeInput(TRIGGER_BUTTON, 0), "Left Trigger Button"));
void ViveControllerManager::buildDeviceProxy(controller::DeviceProxy::Pointer proxy) {
using namespace controller;
proxy->_name = _name;
proxy->getButton = [this](const Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); };
proxy->getAxis = [this](const Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); };
proxy->getPose = [this](const Input& input, int timestamp) -> Pose { return this->getPose(input.getChannel()); };
proxy->getAvailabeInputs = [this]() -> QVector<Input::NamedPair> {
QVector<Input::NamedPair> availableInputs{
// Trackpad analogs
makePair(LX, "LX"),
makePair(LY, "LY"),
makePair(RX, "RX"),
makePair(RY, "RY"),
// trigger analogs
makePair(LT, "LT"),
makePair(RT, "RT"),
availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_POS, 0), "Left Trackpad Up"));
availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_NEG, 0), "Left Trackpad Down"));
availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_POS, 0), "Left Trackpad Right"));
availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_NEG, 0), "Left Trackpad Left"));
availableInputs.append(UserInputMapper::InputPair(makeInput(BACK_TRIGGER, 0), "Left Back Trigger"));
makePair(LB, "LB"),
makePair(RB, "RB"),
availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_HAND), "Right Hand"));
makePair(LS, "LS"),
makePair(RS, "RS"),
};
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_A, 1), "Right Button A"));
availableInputs.append(UserInputMapper::InputPair(makeInput(GRIP_BUTTON, 1), "Right Grip Button"));
availableInputs.append(UserInputMapper::InputPair(makeInput(TRACKPAD_BUTTON, 1), "Right Trackpad Button"));
availableInputs.append(UserInputMapper::InputPair(makeInput(TRIGGER_BUTTON, 1), "Right Trigger Button"));
//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"));
//availableInputs.append(Input::NamedPair(makeInput(TRIGGER_BUTTON, 0), "Left Trigger Button"));
//availableInputs.append(Input::NamedPair(makeInput(AXIS_Y_POS, 0), "Left Trackpad Up"));
//availableInputs.append(Input::NamedPair(makeInput(AXIS_Y_NEG, 0), "Left Trackpad Down"));
//availableInputs.append(Input::NamedPair(makeInput(AXIS_X_POS, 0), "Left Trackpad Right"));
//availableInputs.append(Input::NamedPair(makeInput(AXIS_X_NEG, 0), "Left Trackpad Left"));
//availableInputs.append(Input::NamedPair(makeInput(BACK_TRIGGER, 0), "Left Back Trigger"));
//availableInputs.append(Input::NamedPair(makeInput(RIGHT_HAND), "Right Hand"));
//availableInputs.append(Input::NamedPair(makeInput(BUTTON_A, 1), "Right Button A"));
//availableInputs.append(Input::NamedPair(makeInput(GRIP_BUTTON, 1), "Right Grip Button"));
//availableInputs.append(Input::NamedPair(makeInput(TRACKPAD_BUTTON, 1), "Right Trackpad Button"));
//availableInputs.append(Input::NamedPair(makeInput(TRIGGER_BUTTON, 1), "Right Trigger Button"));
//availableInputs.append(Input::NamedPair(makeInput(AXIS_Y_POS, 1), "Right Trackpad Up"));
//availableInputs.append(Input::NamedPair(makeInput(AXIS_Y_NEG, 1), "Right Trackpad Down"));
//availableInputs.append(Input::NamedPair(makeInput(AXIS_X_POS, 1), "Right Trackpad Right"));
//availableInputs.append(Input::NamedPair(makeInput(AXIS_X_NEG, 1), "Right Trackpad Left"));
//availableInputs.append(Input::NamedPair(makeInput(BACK_TRIGGER, 1), "Right Back Trigger"));
availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_POS, 1), "Right Trackpad Up"));
availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_NEG, 1), "Right Trackpad Down"));
availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_POS, 1), "Right Trackpad Right"));
availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_NEG, 1), "Right Trackpad Left"));
availableInputs.append(UserInputMapper::InputPair(makeInput(BACK_TRIGGER, 1), "Right Back Trigger"));
return availableInputs;
};
proxy->resetDeviceBindings = [this, &mapper] () -> bool {
mapper.removeAllInputChannelsForDevice(_deviceID);
this->assignDefaultInputMapping(mapper);
return true;
};
mapper.registerDevice(_deviceID, proxy);
}
void ViveControllerManager::assignDefaultInputMapping(UserInputMapper& mapper) {
const float JOYSTICK_MOVE_SPEED = 1.0f;
// Left Trackpad: Movement, strafing
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(AXIS_Y_POS, 0), makeInput(TRACKPAD_BUTTON, 0), JOYSTICK_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(AXIS_Y_NEG, 0), makeInput(TRACKPAD_BUTTON, 0), JOYSTICK_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(AXIS_X_POS, 0), makeInput(TRACKPAD_BUTTON, 0), JOYSTICK_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(AXIS_X_NEG, 0), makeInput(TRACKPAD_BUTTON, 0), JOYSTICK_MOVE_SPEED);
// Right Trackpad: Vertical movement, zooming
mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(AXIS_Y_POS, 1), makeInput(TRACKPAD_BUTTON, 1), JOYSTICK_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(AXIS_Y_NEG, 1), makeInput(TRACKPAD_BUTTON, 1), JOYSTICK_MOVE_SPEED);
// Buttons
mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(BUTTON_A, 0));
mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(BUTTON_A, 1));
mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(GRIP_BUTTON, 0));
mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(GRIP_BUTTON, 1));
mapper.addInputChannel(UserInputMapper::LEFT_HAND_CLICK, makeInput(BACK_TRIGGER, 0));
mapper.addInputChannel(UserInputMapper::RIGHT_HAND_CLICK, makeInput(BACK_TRIGGER, 1));
// Hands
mapper.addInputChannel(UserInputMapper::LEFT_HAND, makeInput(LEFT_HAND));
mapper.addInputChannel(UserInputMapper::RIGHT_HAND, makeInput(RIGHT_HAND));
QString ViveControllerManager::getDefaultMappingConfig() {
static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/vive.json";
return MAPPING_JSON;
}
UserInputMapper::Input ViveControllerManager::makeInput(unsigned int button, int index) {
return UserInputMapper::Input(_deviceID, button | (index == 0 ? LEFT_MASK : RIGHT_MASK), UserInputMapper::ChannelType::BUTTON);
}
UserInputMapper::Input ViveControllerManager::makeInput(ViveControllerManager::JoystickAxisChannel axis, int index) {
return UserInputMapper::Input(_deviceID, axis | (index == 0 ? LEFT_MASK : RIGHT_MASK), UserInputMapper::ChannelType::AXIS);
}
UserInputMapper::Input ViveControllerManager::makeInput(JointChannel joint) {
return UserInputMapper::Input(_deviceID, joint, UserInputMapper::ChannelType::POSE);
}
//void ViveControllerManager::assignDefaultInputMapping(UserInputMapper& mapper) {
// const float JOYSTICK_MOVE_SPEED = 1.0f;
//
// // Left Trackpad: Movement, strafing
// mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(AXIS_Y_POS, 0), makeInput(TRACKPAD_BUTTON, 0), JOYSTICK_MOVE_SPEED);
// mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(AXIS_Y_NEG, 0), makeInput(TRACKPAD_BUTTON, 0), JOYSTICK_MOVE_SPEED);
// mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(AXIS_X_POS, 0), makeInput(TRACKPAD_BUTTON, 0), JOYSTICK_MOVE_SPEED);
// mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(AXIS_X_NEG, 0), makeInput(TRACKPAD_BUTTON, 0), JOYSTICK_MOVE_SPEED);
//
// // Right Trackpad: Vertical movement, zooming
// mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(AXIS_Y_POS, 1), makeInput(TRACKPAD_BUTTON, 1), JOYSTICK_MOVE_SPEED);
// mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(AXIS_Y_NEG, 1), makeInput(TRACKPAD_BUTTON, 1), JOYSTICK_MOVE_SPEED);
//
// // Buttons
// mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(BUTTON_A, 0));
// mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(BUTTON_A, 1));
//
// mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(GRIP_BUTTON, 0));
// mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(GRIP_BUTTON, 1));
//
// mapper.addInputChannel(UserInputMapper::LEFT_HAND_CLICK, makeInput(BACK_TRIGGER, 0));
// mapper.addInputChannel(UserInputMapper::RIGHT_HAND_CLICK, makeInput(BACK_TRIGGER, 1));
//
// // Hands
// mapper.addInputChannel(UserInputMapper::LEFT_HAND, makeInput(LEFT_HAND));
// mapper.addInputChannel(UserInputMapper::RIGHT_HAND, makeInput(RIGHT_HAND));
//}

View file

@ -24,30 +24,9 @@
#include <RenderArgs.h>
#include <render/Scene.h>
class ViveControllerManager : public InputPlugin, public InputDevice {
class ViveControllerManager : public InputPlugin, public controller::InputDevice {
Q_OBJECT
public:
enum JoystickAxisChannel {
AXIS_Y_POS = 1U << 1,
AXIS_Y_NEG = 1U << 2,
AXIS_X_POS = 1U << 3,
AXIS_X_NEG = 1U << 4,
BACK_TRIGGER = 1U << 5,
};
enum Axis {
TRACKPAD_AXIS = 0,
TRIGGER_AXIS,
AXIS_2,
AXIS_3,
AXIS_4,
};
enum JointChannel {
LEFT_HAND = 0,
RIGHT_HAND,
};
ViveControllerManager();
// Plugin functions
@ -62,8 +41,8 @@ public:
virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override { update(deltaTime, jointsCaptured); }
// Device functions
virtual void registerToUserInputMapper(UserInputMapper& mapper) override;
virtual void assignDefaultInputMapping(UserInputMapper& mapper) override;
virtual void buildDeviceProxy(controller::DeviceProxy::Pointer proxy) override;
virtual QString getDefaultMappingConfig() override;
virtual void update(float deltaTime, bool jointsCaptured) override;
virtual void focusOutEvent() override;
@ -71,16 +50,12 @@ public:
void setRenderControllers(bool renderControllers) { _renderControllers = renderControllers; }
UserInputMapper::Input makeInput(unsigned int button, int index);
UserInputMapper::Input makeInput(JoystickAxisChannel axis, int index);
UserInputMapper::Input makeInput(JointChannel joint);
private:
void renderHand(UserInputMapper::PoseValue pose, gpu::Batch& batch, int index);
void renderHand(const controller::Pose& pose, gpu::Batch& batch, int sign);
void handleButtonEvent(uint64_t buttons, int index);
void handleAxisEvent(Axis axis, float x, float y, int index);
void handlePoseEvent(const mat4& mat, int index);
void handleButtonEvent(uint32_t button, bool pressed, bool left);
void handleAxisEvent(uint32_t axis, float x, float y, bool left);
void handlePoseEvent(const mat4& mat, bool left);
int _trackedControllers;

View file

@ -28,7 +28,9 @@
#include <udt/PacketHeaders.h>
#include <UUID.h>
#include "AnimationObject.h"
#include <controllers/ScriptingInterface.h>
#include <AnimationObject.h>
#include "ArrayBufferViewClass.h"
#include "BatchLoader.h"
#include "DataViewClass.h"

View file

@ -26,8 +26,6 @@
#include <LimitedNodeList.h>
#include <EntityItemID.h>
#include <EntitiesScriptEngineProvider.h>
#include <controllers/ScriptingInterface.h>
#include "MouseEvent.h"
#include "ArrayBufferClass.h"

View file

@ -123,20 +123,20 @@ int main(int argc, char** argv) {
inputPlugin->pluginUpdate(delta, false);
}
auto userInputMapper = DependencyManager::get<UserInputMapper>();
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
userInputMapper->update(delta);
});
timer.start(50);
{
DependencyManager::set<UserInputMapper>();
DependencyManager::set<controller::UserInputMapper>();
foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) {
QString name = inputPlugin->getName();
inputPlugin->activate();
auto userInputMapper = DependencyManager::get<UserInputMapper>();
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
if (name == KeyboardMouseDevice::NAME) {
auto keyboardMouseDevice = static_cast<KeyboardMouseDevice*>(inputPlugin.data()); // TODO: this seems super hacky
keyboardMouseDevice->registerToUserInputMapper(*userInputMapper);
userInputMapper->registerDevice(keyboardMouseDevice);
}
inputPlugin->pluginUpdate(0, false);
}