mirror of
https://github.com/overte-org/overte.git
synced 2025-04-15 10:08:46 +02:00
Merge pull request #4753 from samcake/orange
Introducing UserInputMapper
This commit is contained in:
commit
a039d5f28c
7 changed files with 738 additions and 83 deletions
|
@ -547,6 +547,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
connect(&_myAvatar->getSkeletonModel(), &SkeletonModel::skeletonLoaded,
|
||||
this, &Application::checkSkeleton, Qt::QueuedConnection);
|
||||
|
||||
// Setup the userInputMapper with the actions
|
||||
// Setup the keyboardMouseDevice and the user input mapper with the default bindings
|
||||
_keyboardMouseDevice.registerToUserInputMapper(_userInputMapper);
|
||||
_keyboardMouseDevice.assignDefaultInputMapping(_userInputMapper);
|
||||
|
||||
// check first run...
|
||||
if (_firstRun.get()) {
|
||||
qCDebug(interfaceapp) << "This is a first run...";
|
||||
|
@ -1088,6 +1093,8 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
}
|
||||
|
||||
if (activeWindow() == _window) {
|
||||
_keyboardMouseDevice.keyPressEvent(event);
|
||||
|
||||
bool isShifted = event->modifiers().testFlag(Qt::ShiftModifier);
|
||||
bool isMeta = event->modifiers().testFlag(Qt::ControlModifier);
|
||||
bool isOption = event->modifiers().testFlag(Qt::AltModifier);
|
||||
|
@ -1108,11 +1115,6 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
}
|
||||
break;
|
||||
|
||||
case Qt::Key_E:
|
||||
case Qt::Key_PageUp:
|
||||
_myAvatar->setDriveKeys(UP, 1.0f);
|
||||
break;
|
||||
|
||||
case Qt::Key_F: {
|
||||
_physicsEngine.dumpNextStats();
|
||||
break;
|
||||
|
@ -1122,16 +1124,9 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
Menu::getInstance()->triggerOption(MenuOption::Stars);
|
||||
break;
|
||||
|
||||
case Qt::Key_C:
|
||||
case Qt::Key_PageDown:
|
||||
_myAvatar->setDriveKeys(DOWN, 1.0f);
|
||||
break;
|
||||
|
||||
case Qt::Key_W:
|
||||
if (isOption && !isShifted && !isMeta) {
|
||||
Menu::getInstance()->triggerOption(MenuOption::Wireframe);
|
||||
} else {
|
||||
_myAvatar->setDriveKeys(FWD, 1.0f);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1142,8 +1137,6 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
Menu::getInstance()->triggerOption(MenuOption::ScriptEditor);
|
||||
} else if (!isOption && !isShifted && isMeta) {
|
||||
takeSnapshot();
|
||||
} else {
|
||||
_myAvatar->setDriveKeys(BACK, 1.0f);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1154,14 +1147,6 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
case Qt::Key_A:
|
||||
if (isShifted) {
|
||||
Menu::getInstance()->triggerOption(MenuOption::Atmosphere);
|
||||
} else if (!isMeta) {
|
||||
_myAvatar->setDriveKeys(ROT_LEFT, 1.0f);
|
||||
}
|
||||
break;
|
||||
|
||||
case Qt::Key_D:
|
||||
if (!isMeta) {
|
||||
_myAvatar->setDriveKeys(ROT_RIGHT, 1.0f);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1176,8 +1161,6 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
} else {
|
||||
_raiseMirror += 0.05f;
|
||||
}
|
||||
} else {
|
||||
_myAvatar->setDriveKeys(isShifted ? UP : FWD, 1.0f);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1188,24 +1171,18 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
} else {
|
||||
_raiseMirror -= 0.05f;
|
||||
}
|
||||
} else {
|
||||
_myAvatar->setDriveKeys(isShifted ? DOWN : BACK, 1.0f);
|
||||
}
|
||||
break;
|
||||
|
||||
case Qt::Key_Left:
|
||||
if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||
_rotateMirror += PI / 20.0f;
|
||||
} else {
|
||||
_myAvatar->setDriveKeys(isShifted ? LEFT : ROT_LEFT, 1.0f);
|
||||
}
|
||||
break;
|
||||
|
||||
case Qt::Key_Right:
|
||||
if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||
_rotateMirror -= PI / 20.0f;
|
||||
} else {
|
||||
_myAvatar->setDriveKeys(isShifted ? RIGHT : ROT_RIGHT, 1.0f);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1342,65 +1319,15 @@ void Application::keyReleaseEvent(QKeyEvent* event) {
|
|||
_keysPressed.remove(event->key());
|
||||
|
||||
_controllerScriptingInterface.emitKeyReleaseEvent(event); // send events to any registered scripts
|
||||
|
||||
|
||||
// if one of our scripts have asked to capture this event, then stop processing it
|
||||
if (_controllerScriptingInterface.isKeyCaptured(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
_keyboardMouseDevice.keyReleaseEvent(event);
|
||||
|
||||
switch (event->key()) {
|
||||
case Qt::Key_E:
|
||||
case Qt::Key_PageUp:
|
||||
_myAvatar->setDriveKeys(UP, 0.0f);
|
||||
break;
|
||||
|
||||
case Qt::Key_C:
|
||||
case Qt::Key_PageDown:
|
||||
_myAvatar->setDriveKeys(DOWN, 0.0f);
|
||||
break;
|
||||
|
||||
case Qt::Key_W:
|
||||
_myAvatar->setDriveKeys(FWD, 0.0f);
|
||||
break;
|
||||
|
||||
case Qt::Key_S:
|
||||
_myAvatar->setDriveKeys(BACK, 0.0f);
|
||||
break;
|
||||
|
||||
case Qt::Key_A:
|
||||
_myAvatar->setDriveKeys(ROT_LEFT, 0.0f);
|
||||
break;
|
||||
|
||||
case Qt::Key_D:
|
||||
_myAvatar->setDriveKeys(ROT_RIGHT, 0.0f);
|
||||
break;
|
||||
|
||||
case Qt::Key_Up:
|
||||
_myAvatar->setDriveKeys(FWD, 0.0f);
|
||||
_myAvatar->setDriveKeys(UP, 0.0f);
|
||||
break;
|
||||
|
||||
case Qt::Key_Down:
|
||||
_myAvatar->setDriveKeys(BACK, 0.0f);
|
||||
_myAvatar->setDriveKeys(DOWN, 0.0f);
|
||||
break;
|
||||
|
||||
case Qt::Key_Left:
|
||||
_myAvatar->setDriveKeys(LEFT, 0.0f);
|
||||
_myAvatar->setDriveKeys(ROT_LEFT, 0.0f);
|
||||
break;
|
||||
|
||||
case Qt::Key_Right:
|
||||
_myAvatar->setDriveKeys(RIGHT, 0.0f);
|
||||
_myAvatar->setDriveKeys(ROT_RIGHT, 0.0f);
|
||||
break;
|
||||
case Qt::Key_Control:
|
||||
case Qt::Key_Shift:
|
||||
case Qt::Key_Meta:
|
||||
case Qt::Key_Alt:
|
||||
_myAvatar->clearDriveKeys();
|
||||
break;
|
||||
case Qt::Key_Space: {
|
||||
if (!event->isAutoRepeat()) {
|
||||
// this ends the HFActionEvent
|
||||
|
@ -1426,6 +1353,8 @@ void Application::keyReleaseEvent(QKeyEvent* event) {
|
|||
}
|
||||
|
||||
void Application::focusOutEvent(QFocusEvent* event) {
|
||||
_keyboardMouseDevice.focusOutEvent(event);
|
||||
|
||||
// synthesize events for keys currently pressed, since we may not get their release events
|
||||
foreach (int key, _keysPressed) {
|
||||
QKeyEvent event(QEvent::KeyRelease, key, Qt::NoModifier);
|
||||
|
@ -1464,6 +1393,8 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) {
|
|||
if (_controllerScriptingInterface.isMouseCaptured()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_keyboardMouseDevice.mouseMoveEvent(event, deviceID);
|
||||
|
||||
}
|
||||
|
||||
|
@ -1484,6 +1415,8 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) {
|
|||
|
||||
|
||||
if (activeWindow() == _window) {
|
||||
_keyboardMouseDevice.mousePressEvent(event);
|
||||
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
_mouseDragStartedX = getTrueMouseX();
|
||||
_mouseDragStartedY = getTrueMouseY();
|
||||
|
@ -1526,6 +1459,8 @@ void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) {
|
|||
}
|
||||
|
||||
if (activeWindow() == _window) {
|
||||
_keyboardMouseDevice.mouseReleaseEvent(event);
|
||||
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
_mousePressed = false;
|
||||
|
||||
|
@ -1546,6 +1481,7 @@ void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) {
|
|||
|
||||
void Application::touchUpdateEvent(QTouchEvent* event) {
|
||||
_altPressed = false;
|
||||
|
||||
if (event->type() == QEvent::TouchUpdate) {
|
||||
TouchEvent thisEvent(*event, _lastTouchEvent);
|
||||
_controllerScriptingInterface.emitTouchUpdateEvent(thisEvent); // send events to any registered scripts
|
||||
|
@ -1557,6 +1493,8 @@ void Application::touchUpdateEvent(QTouchEvent* event) {
|
|||
return;
|
||||
}
|
||||
|
||||
_keyboardMouseDevice.touchUpdateEvent(event);
|
||||
|
||||
bool validTouch = false;
|
||||
if (activeWindow() == _window) {
|
||||
const QList<QTouchEvent::TouchPoint>& tPoints = event->touchPoints();
|
||||
|
@ -1593,6 +1531,8 @@ void Application::touchBeginEvent(QTouchEvent* event) {
|
|||
return;
|
||||
}
|
||||
|
||||
_keyboardMouseDevice.touchBeginEvent(event);
|
||||
|
||||
}
|
||||
|
||||
void Application::touchEndEvent(QTouchEvent* event) {
|
||||
|
@ -1605,6 +1545,9 @@ void Application::touchEndEvent(QTouchEvent* event) {
|
|||
if (_controllerScriptingInterface.isTouchCaptured()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_keyboardMouseDevice.touchEndEvent(event);
|
||||
|
||||
// put any application specific touch behavior below here..
|
||||
_touchDragStartedAvgX = _touchAvgX;
|
||||
_touchDragStartedAvgY = _touchAvgY;
|
||||
|
@ -1620,6 +1563,8 @@ void Application::wheelEvent(QWheelEvent* event) {
|
|||
if (_controllerScriptingInterface.isWheelCaptured()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_keyboardMouseDevice.wheelEvent(event);
|
||||
}
|
||||
|
||||
void Application::dropEvent(QDropEvent *event) {
|
||||
|
@ -2356,6 +2301,7 @@ void Application::update(float deltaTime) {
|
|||
|
||||
updateLOD();
|
||||
updateMouseRay(); // check what's under the mouse and update the mouse voxel
|
||||
|
||||
{
|
||||
PerformanceTimer perfTimer("devices");
|
||||
DeviceTracker::updateAll();
|
||||
|
@ -2366,10 +2312,27 @@ void Application::update(float deltaTime) {
|
|||
SixenseManager::getInstance().update(deltaTime);
|
||||
JoystickScriptingInterface::getInstance().update();
|
||||
}
|
||||
|
||||
|
||||
_userInputMapper.update(deltaTime);
|
||||
_keyboardMouseDevice.update();
|
||||
|
||||
// Dispatch input events
|
||||
_controllerScriptingInterface.updateInputControllers();
|
||||
|
||||
// Transfer the user inputs to the driveKeys
|
||||
_myAvatar->clearDriveKeys();
|
||||
_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(ROT_UP, _userInputMapper.getActionState(UserInputMapper::PITCH_UP));
|
||||
_myAvatar->setDriveKeys(ROT_DOWN, _userInputMapper.getActionState(UserInputMapper::PITCH_DOWN));
|
||||
_myAvatar->setDriveKeys(ROT_LEFT, _userInputMapper.getActionState(UserInputMapper::YAW_LEFT));
|
||||
_myAvatar->setDriveKeys(ROT_RIGHT, _userInputMapper.getActionState(UserInputMapper::YAW_RIGHT));
|
||||
|
||||
|
||||
updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process...
|
||||
|
||||
DependencyManager::get<AvatarManager>()->updateOtherAvatars(deltaTime); //loop through all the other avatars and simulate them...
|
||||
|
|
|
@ -69,6 +69,8 @@
|
|||
#include "ui/ApplicationOverlay.h"
|
||||
#include "ui/RunningScriptsWidget.h"
|
||||
#include "ui/ToolWindow.h"
|
||||
#include "ui/UserInputMapper.h"
|
||||
#include "devices/KeyboardMouseDevice.h"
|
||||
#include "octree/OctreeFade.h"
|
||||
#include "octree/OctreePacketProcessor.h"
|
||||
#include "UndoStackScriptingInterface.h"
|
||||
|
@ -517,6 +519,10 @@ private:
|
|||
|
||||
OctreeQuery _octreeQuery; // NodeData derived class for querying octee cells from octree servers
|
||||
|
||||
KeyboardMouseDevice _keyboardMouseDevice; // Default input device, the good old keyboard mouse and maybe touchpad
|
||||
|
||||
UserInputMapper _userInputMapper; // User input mapper allowing to mapp different real devices to the action channels that the application has to offer
|
||||
|
||||
MyAvatar* _myAvatar; // TODO: move this and relevant code to AvatarManager (or MyAvatar as the case may be)
|
||||
|
||||
Camera _myCamera; // My view onto the world
|
||||
|
|
253
interface/src/devices/KeyboardMouseDevice.cpp
Executable file
253
interface/src/devices/KeyboardMouseDevice.cpp
Executable file
|
@ -0,0 +1,253 @@
|
|||
|
||||
//
|
||||
// KeyboardMouseDevice.cpp
|
||||
// interface/src/devices
|
||||
//
|
||||
// Created by Sam Gateau on 4/27/15.
|
||||
// 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 "KeyboardMouseDevice.h"
|
||||
|
||||
void KeyboardMouseDevice::update() {
|
||||
_axisStateMap.clear();
|
||||
|
||||
// For touch event, we need to check that the last event is not too long ago
|
||||
// Maybe it's a Qt issue, but the touch event sequence (begin, update, end) is not always called properly
|
||||
// The following is a workaround to detect that the touch sequence is over in case we didn;t see the end event
|
||||
if (_isTouching) {
|
||||
const auto TOUCH_EVENT_MAXIMUM_WAIT = 100; //ms
|
||||
auto currentTime = _clock.now();
|
||||
auto sinceLastTouch = std::chrono::duration_cast<std::chrono::milliseconds>(currentTime - _lastTouchTime);
|
||||
if (sinceLastTouch.count() > TOUCH_EVENT_MAXIMUM_WAIT) {
|
||||
_isTouching = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardMouseDevice::focusOutEvent(QFocusEvent* event) {
|
||||
_buttonPressedMap.clear();
|
||||
};
|
||||
|
||||
void KeyboardMouseDevice::keyPressEvent(QKeyEvent* event) {
|
||||
auto input = makeInput((Qt::Key) event->key());
|
||||
auto result = _buttonPressedMap.insert(input.getChannel());
|
||||
if (!result.second) {
|
||||
// key pressed again ? without catching the release event ?
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardMouseDevice::keyReleaseEvent(QKeyEvent* event) {
|
||||
auto input = makeInput((Qt::Key) event->key());
|
||||
_buttonPressedMap.erase(input.getChannel());
|
||||
}
|
||||
|
||||
void KeyboardMouseDevice::mousePressEvent(QMouseEvent* event, unsigned int deviceID) {
|
||||
auto input = makeInput((Qt::MouseButton) event->button());
|
||||
auto result = _buttonPressedMap.insert(input.getChannel());
|
||||
if (!result.second) {
|
||||
// key pressed again ? without catching the release event ?
|
||||
}
|
||||
_lastCursor = event->pos();
|
||||
}
|
||||
|
||||
void KeyboardMouseDevice::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) {
|
||||
auto input = makeInput((Qt::MouseButton) event->button());
|
||||
_buttonPressedMap.erase(input.getChannel());
|
||||
}
|
||||
|
||||
void KeyboardMouseDevice::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) {
|
||||
QPoint currentPos = event->pos();
|
||||
QPoint currentMove = currentPos - _lastCursor;
|
||||
|
||||
_axisStateMap[makeInput(MOUSE_AXIS_X_POS).getChannel()] = (currentMove.x() > 0 ? currentMove.x() : 0.0f);
|
||||
_axisStateMap[makeInput(MOUSE_AXIS_X_NEG).getChannel()] = (currentMove.x() < 0 ? -currentMove.x() : 0.0f);
|
||||
// Y mouse is inverted positive is pointing up the screen
|
||||
_axisStateMap[makeInput(MOUSE_AXIS_Y_POS).getChannel()] = (currentMove.y() < 0 ? -currentMove.y() : 0.0f);
|
||||
_axisStateMap[makeInput(MOUSE_AXIS_Y_NEG).getChannel()] = (currentMove.y() > 0 ? currentMove.y() : 0.0f);
|
||||
|
||||
_lastCursor = currentPos;
|
||||
}
|
||||
|
||||
void KeyboardMouseDevice::wheelEvent(QWheelEvent* event) {
|
||||
auto currentMove = event->angleDelta() / 120.0f;
|
||||
|
||||
_axisStateMap[makeInput(MOUSE_AXIS_WHEEL_X_POS).getChannel()] = (currentMove.x() > 0 ? currentMove.x() : 0.0f);
|
||||
_axisStateMap[makeInput(MOUSE_AXIS_WHEEL_X_NEG).getChannel()] = (currentMove.x() < 0 ? -currentMove.x() : 0.0f);
|
||||
_axisStateMap[makeInput(MOUSE_AXIS_WHEEL_Y_POS).getChannel()] = (currentMove.y() > 0 ? currentMove.y() : 0.0f);
|
||||
_axisStateMap[makeInput(MOUSE_AXIS_WHEEL_Y_NEG).getChannel()] = (currentMove.y() < 0 ? -currentMove.y() : 0.0f);
|
||||
}
|
||||
|
||||
glm::vec2 KeyboardMouseDevice::evalAverageTouchPoints(const QList<QTouchEvent::TouchPoint>& points) const {
|
||||
glm::vec2 averagePoint(0.0f);
|
||||
if (points.count() > 0) {
|
||||
for (auto& point : points) {
|
||||
averagePoint += glm::vec2(point.pos().x(), point.pos().y());
|
||||
}
|
||||
averagePoint /= (float)(points.count());
|
||||
}
|
||||
return averagePoint;
|
||||
}
|
||||
|
||||
void KeyboardMouseDevice::touchBeginEvent(const QTouchEvent* event) {
|
||||
_isTouching = event->touchPointStates().testFlag(Qt::TouchPointPressed);
|
||||
_lastTouch = evalAverageTouchPoints(event->touchPoints());
|
||||
_lastTouchTime = _clock.now();
|
||||
}
|
||||
|
||||
void KeyboardMouseDevice::touchEndEvent(const QTouchEvent* event) {
|
||||
_isTouching = false;
|
||||
_lastTouch = evalAverageTouchPoints(event->touchPoints());
|
||||
_lastTouchTime = _clock.now();
|
||||
}
|
||||
|
||||
void KeyboardMouseDevice::touchUpdateEvent(const QTouchEvent* event) {
|
||||
auto currentPos = evalAverageTouchPoints(event->touchPoints());
|
||||
_lastTouchTime = _clock.now();
|
||||
|
||||
if (!_isTouching) {
|
||||
_isTouching = event->touchPointStates().testFlag(Qt::TouchPointPressed);
|
||||
} else {
|
||||
auto currentMove = currentPos - _lastTouch;
|
||||
|
||||
_axisStateMap[makeInput(TOUCH_AXIS_X_POS).getChannel()] = (currentMove.x > 0 ? currentMove.x : 0.0f);
|
||||
_axisStateMap[makeInput(TOUCH_AXIS_X_NEG).getChannel()] = (currentMove.x < 0 ? -currentMove.x : 0.0f);
|
||||
// Y mouse is inverted positive is pointing up the screen
|
||||
_axisStateMap[makeInput(TOUCH_AXIS_Y_POS).getChannel()] = (currentMove.y < 0 ? -currentMove.y : 0.0f);
|
||||
_axisStateMap[makeInput(TOUCH_AXIS_Y_NEG).getChannel()] = (currentMove.y > 0 ? currentMove.y : 0.0f);
|
||||
}
|
||||
|
||||
_lastTouch = currentPos;
|
||||
}
|
||||
|
||||
UserInputMapper::Input KeyboardMouseDevice::makeInput(Qt::Key code) {
|
||||
return UserInputMapper::Input(_deviceID, code & KEYBOARD_MASK, UserInputMapper::ChannelType::BUTTON);
|
||||
}
|
||||
|
||||
UserInputMapper::Input KeyboardMouseDevice::makeInput(Qt::MouseButton code) {
|
||||
switch (code) {
|
||||
case Qt::LeftButton:
|
||||
return UserInputMapper::Input(_deviceID, MOUSE_BUTTON_LEFT, UserInputMapper::ChannelType::BUTTON);
|
||||
case Qt::RightButton:
|
||||
return UserInputMapper::Input(_deviceID, MOUSE_BUTTON_RIGHT, UserInputMapper::ChannelType::BUTTON);
|
||||
case Qt::MiddleButton:
|
||||
return UserInputMapper::Input(_deviceID, MOUSE_BUTTON_MIDDLE, UserInputMapper::ChannelType::BUTTON);
|
||||
default:
|
||||
return UserInputMapper::Input();
|
||||
};
|
||||
}
|
||||
|
||||
UserInputMapper::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::MouseAxisChannel axis) {
|
||||
return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS);
|
||||
}
|
||||
|
||||
UserInputMapper::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::TouchAxisChannel axis) {
|
||||
return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS);
|
||||
}
|
||||
|
||||
UserInputMapper::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::TouchButtonChannel button) {
|
||||
return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON);
|
||||
}
|
||||
|
||||
void KeyboardMouseDevice::registerToUserInputMapper(UserInputMapper& mapper) {
|
||||
// Grab the current free device ID
|
||||
_deviceID = mapper.getFreeDeviceID();
|
||||
|
||||
auto proxy = UserInputMapper::DeviceProxy::Pointer(new UserInputMapper::DeviceProxy());
|
||||
proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input._channel); };
|
||||
proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input._channel); };
|
||||
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::LATERAL_LEFT, makeInput(Qt::Key_A), BUTTON_MOVE_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_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_W), makeInput(Qt::Key_Shift), BUTTON_BOOM_SPEED);
|
||||
// mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(Qt::Key_S), makeInput(Qt::Key_Shift), BUTTON_BOOM_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(Qt::Key_A), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(Qt::Key_D), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(Qt::Key_C), makeInput(Qt::Key_Shift), BUTTON_PITCH_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(Qt::Key_E), 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::LATERAL_LEFT, makeInput(Qt::Key_Left), BUTTON_MOVE_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_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::BOOM_IN, makeInput(Qt::Key_Up), makeInput(Qt::Key_Shift), BUTTON_BOOM_SPEED);
|
||||
// mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(Qt::Key_Down), makeInput(Qt::Key_Shift), BUTTON_BOOM_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(Qt::Key_Left), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::YAW_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_NEG), BUTTON_BOOM_SPEED);
|
||||
//mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(MOUSE_AXIS_WHEEL_Y_POS), 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
|
||||
|
||||
}
|
||||
|
||||
float KeyboardMouseDevice::getButton(int channel) const {
|
||||
if (!_buttonPressedMap.empty()) {
|
||||
if (_buttonPressedMap.find(channel) != _buttonPressedMap.end()) {
|
||||
return 1.0f;
|
||||
} else {
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
float KeyboardMouseDevice::getAxis(int channel) const {
|
||||
auto axis = _axisStateMap.find(channel);
|
||||
if (axis != _axisStateMap.end()) {
|
||||
return (*axis).second;
|
||||
} else {
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
108
interface/src/devices/KeyboardMouseDevice.h
Executable file
108
interface/src/devices/KeyboardMouseDevice.h
Executable file
|
@ -0,0 +1,108 @@
|
|||
//
|
||||
// KeyboardMouseDevice.h
|
||||
// interface/src/devices
|
||||
//
|
||||
// Created by Sam Gateau on 4/27/15.
|
||||
// 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
|
||||
//
|
||||
|
||||
#ifndef hifi_KeyboardMouseDevice_h
|
||||
#define hifi_KeyboardMouseDevice_h
|
||||
|
||||
#include <QTouchEvent>
|
||||
#include <chrono>
|
||||
#include "ui/UserInputMapper.h"
|
||||
|
||||
class KeyboardMouseDevice {
|
||||
public:
|
||||
|
||||
enum KeyboardChannel {
|
||||
KEYBOARD_FIRST = 0,
|
||||
KEYBOARD_LAST = 255,
|
||||
KEYBOARD_MASK = 0x00FF,
|
||||
};
|
||||
|
||||
enum MouseButtonChannel {
|
||||
MOUSE_BUTTON_LEFT = KEYBOARD_LAST + 1,
|
||||
MOUSE_BUTTON_RIGHT,
|
||||
MOUSE_BUTTON_MIDDLE,
|
||||
};
|
||||
|
||||
enum MouseAxisChannel {
|
||||
MOUSE_AXIS_X_POS = MOUSE_BUTTON_MIDDLE + 1,
|
||||
MOUSE_AXIS_X_NEG,
|
||||
MOUSE_AXIS_Y_POS,
|
||||
MOUSE_AXIS_Y_NEG,
|
||||
MOUSE_AXIS_WHEEL_Y_POS,
|
||||
MOUSE_AXIS_WHEEL_Y_NEG,
|
||||
MOUSE_AXIS_WHEEL_X_POS,
|
||||
MOUSE_AXIS_WHEEL_X_NEG,
|
||||
};
|
||||
|
||||
enum TouchAxisChannel {
|
||||
TOUCH_AXIS_X_POS = MOUSE_AXIS_WHEEL_X_NEG + 1,
|
||||
TOUCH_AXIS_X_NEG,
|
||||
TOUCH_AXIS_Y_POS,
|
||||
TOUCH_AXIS_Y_NEG,
|
||||
};
|
||||
|
||||
enum TouchButtonChannel {
|
||||
TOUCH_BUTTON_PRESS = TOUCH_AXIS_Y_NEG + 1,
|
||||
};
|
||||
|
||||
typedef std::unordered_set<int> ButtonPressedMap;
|
||||
typedef std::map<int, float> AxisStateMap; // 8 axes
|
||||
|
||||
void focusOutEvent(QFocusEvent* event);
|
||||
|
||||
void keyPressEvent(QKeyEvent* event);
|
||||
void keyReleaseEvent(QKeyEvent* event);
|
||||
|
||||
void mouseMoveEvent(QMouseEvent* event, unsigned int deviceID = 0);
|
||||
void mousePressEvent(QMouseEvent* event, unsigned int deviceID = 0);
|
||||
void mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID = 0);
|
||||
|
||||
void touchBeginEvent(const QTouchEvent* event);
|
||||
void touchEndEvent(const QTouchEvent* event);
|
||||
void touchUpdateEvent(const QTouchEvent* event);
|
||||
|
||||
void wheelEvent(QWheelEvent* event);
|
||||
|
||||
// Get current state for each channels
|
||||
float getButton(int channel) const;
|
||||
float getAxis(int channel) const;
|
||||
|
||||
// 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);
|
||||
|
||||
KeyboardMouseDevice() {}
|
||||
|
||||
void registerToUserInputMapper(UserInputMapper& mapper);
|
||||
void assignDefaultInputMapping(UserInputMapper& mapper);
|
||||
|
||||
// Update call MUST be called once per simulation loop
|
||||
// It takes care of updating the action states and deltas
|
||||
void update();
|
||||
|
||||
protected:
|
||||
ButtonPressedMap _buttonPressedMap;
|
||||
AxisStateMap _axisStateMap;
|
||||
|
||||
int _deviceID = 0;
|
||||
QPoint _lastCursor;
|
||||
glm::vec2 _lastTouch;
|
||||
bool _isTouching = false;
|
||||
|
||||
glm::vec2 evalAverageTouchPoints(const QList<QTouchEvent::TouchPoint>& points) const;
|
||||
std::chrono::high_resolution_clock _clock;
|
||||
std::chrono::high_resolution_clock::time_point _lastTouchTime;
|
||||
};
|
||||
|
||||
#endif // hifi_KeyboardMouseDevice_h
|
146
interface/src/ui/UserInputMapper.cpp
Executable file
146
interface/src/ui/UserInputMapper.cpp
Executable file
|
@ -0,0 +1,146 @@
|
|||
//
|
||||
// UserInputMapper.cpp
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Sam Gateau on 4/27/15.
|
||||
// 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 "UserInputMapper.h"
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
// UserInputMapper Class
|
||||
bool UserInputMapper::registerDevice(uint16 deviceID, const DeviceProxy::Pointer& proxy){
|
||||
_registeredDevices[deviceID] = proxy;
|
||||
return true;
|
||||
}
|
||||
|
||||
UserInputMapper::DeviceProxy::Pointer UserInputMapper::getDeviceProxy(const Input& input) {
|
||||
auto device = _registeredDevices.find(input.getDevice());
|
||||
if (device != _registeredDevices.end()) {
|
||||
return (device->second);
|
||||
} else {
|
||||
return DeviceProxy::Pointer();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int UserInputMapper::getInputChannels(InputChannels& channels) const {
|
||||
for (auto& channel : _actionToInputsMap) {
|
||||
channels.push_back(channel.second);
|
||||
}
|
||||
|
||||
return _actionToInputsMap.size();
|
||||
}
|
||||
|
||||
void UserInputMapper::update(float deltaTime) {
|
||||
|
||||
// Reset the axis state for next loop
|
||||
for (auto& channel : _actionStates) {
|
||||
channel = 0.0f;
|
||||
}
|
||||
|
||||
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::JOINT: {
|
||||
// _channelStates[channelInput.first].jointVal = deviceProxy->getJoint(inputID, currentTimestamp);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break; //silence please
|
||||
}
|
||||
}
|
||||
} else{
|
||||
// Channel input not enabled
|
||||
enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Scale all the channel step with the scale
|
||||
for (auto i = 0; i < NUM_ACTIONS; i++) {
|
||||
_actionStates[i] *= _actionScales[i];
|
||||
}
|
||||
}
|
||||
|
||||
void UserInputMapper::assignDefaulActionScales() {
|
||||
_actionScales[LONGITUDINAL_BACKWARD] = 1.0f; // 1m per unit
|
||||
_actionScales[LONGITUDINAL_FORWARD] = 1.0f; // 1m per unit
|
||||
_actionScales[LATERAL_LEFT] = 1.0f; // 1m per unit
|
||||
_actionScales[LATERAL_RIGHT] = 1.0f; // 1m per unit
|
||||
_actionScales[VERTICAL_DOWN] = 1.0f; // 1m per unit
|
||||
_actionScales[VERTICAL_UP] = 1.0f; // 1m per unit
|
||||
_actionScales[YAW_LEFT] = 1.0f; // 1 degree per unit
|
||||
_actionScales[YAW_RIGHT] = 1.0f; // 1 degree per unit
|
||||
_actionScales[PITCH_DOWN] = 1.0f; // 1 degree per unit
|
||||
_actionScales[PITCH_UP] = 1.0f; // 1 degree per unit
|
||||
_actionScales[BOOM_IN] = 1.0f; // 1m per unit
|
||||
_actionScales[BOOM_OUT] = 1.0f; // 1m per unit
|
||||
}
|
178
interface/src/ui/UserInputMapper.h
Executable file
178
interface/src/ui/UserInputMapper.h
Executable file
|
@ -0,0 +1,178 @@
|
|||
//
|
||||
// UserInputMapper.h
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Sam Gateau on 4/27/15.
|
||||
// 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
|
||||
//
|
||||
|
||||
#ifndef hifi_UserInputMapper_h
|
||||
#define hifi_UserInputMapper_h
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <RegisteredMetaTypes.h>
|
||||
|
||||
#include <unordered_set>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
class UserInputMapper : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
typedef unsigned short uint16;
|
||||
typedef unsigned int uint32;
|
||||
|
||||
enum class ChannelType {
|
||||
UNKNOWN = 0,
|
||||
BUTTON = 1,
|
||||
AXIS,
|
||||
JOINT,
|
||||
};
|
||||
|
||||
// Input is the unique identifier to find a n input channel of a particular device
|
||||
// Devices are responsible for registering to the UseInputMapper so their input channels can be sued and mapped
|
||||
// to the Action channels
|
||||
class Input {
|
||||
public:
|
||||
union {
|
||||
struct {
|
||||
uint16 _device; // Up to 64K possible devices
|
||||
uint16 _channel : 14; // 2^14 possible channel per Device
|
||||
uint16 _type : 2; // 2 bits to store the Type directly in the ID
|
||||
};
|
||||
uint32 _id = 0; // by default Input is 0 meaning invalid
|
||||
};
|
||||
|
||||
bool isValid() const { return (_id != 0); }
|
||||
|
||||
uint16 getDevice() const { return _device; }
|
||||
uint16 getChannel() const { return _channel; }
|
||||
uint32 getID() const { return _id; }
|
||||
|
||||
ChannelType getType() const { return (ChannelType) _type; }
|
||||
bool isButton() const { return getType() == ChannelType::BUTTON; }
|
||||
bool isAxis() const { return getType() == ChannelType::AXIS; }
|
||||
bool isJoint() const { return getType() == ChannelType::JOINT; }
|
||||
|
||||
explicit Input() {}
|
||||
explicit Input(uint32 id) : _id(id) {}
|
||||
explicit Input(uint16 device, uint16 channel, ChannelType type) : _device(device), _channel(channel), _type(uint16(type)) {}
|
||||
Input(const Input& src) : _id(src._id) {}
|
||||
Input& operator = (const Input& src) { _id = src._id; return (*this); }
|
||||
bool operator < (const Input& src) const { return _id < src._id; }
|
||||
};
|
||||
|
||||
// Modifiers are just button inputID
|
||||
typedef std::vector< Input > Modifiers;
|
||||
|
||||
class JointValue {
|
||||
public:
|
||||
glm::vec3 translation{ 0.0f };
|
||||
glm::quat rotation;
|
||||
|
||||
JointValue() {};
|
||||
JointValue(const JointValue&) = default;
|
||||
JointValue& operator = (const JointValue&) = default;
|
||||
};
|
||||
|
||||
typedef std::function<bool (const Input& input, int timestamp)> ButtonGetter;
|
||||
typedef std::function<float (const Input& input, int timestamp)> AxisGetter;
|
||||
typedef std::function<JointValue (const Input& input, int timestamp)> JointGetter;
|
||||
|
||||
class DeviceProxy {
|
||||
public:
|
||||
DeviceProxy() {}
|
||||
|
||||
ButtonGetter getButton = [] (const Input& input, int timestamp) -> bool { return false; };
|
||||
AxisGetter getAxis = [] (const Input& input, int timestamp) -> bool { return 0.0f; };
|
||||
JointGetter getJoint = [] (const Input& input, int timestamp) -> JointValue { return JointValue(); };
|
||||
|
||||
typedef std::shared_ptr<DeviceProxy> Pointer;
|
||||
};
|
||||
// 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);
|
||||
DeviceProxy::Pointer getDeviceProxy(const Input& input);
|
||||
|
||||
|
||||
// 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 {
|
||||
LONGITUDINAL_BACKWARD = 0,
|
||||
LONGITUDINAL_FORWARD,
|
||||
|
||||
LATERAL_LEFT,
|
||||
LATERAL_RIGHT,
|
||||
|
||||
VERTICAL_DOWN,
|
||||
VERTICAL_UP,
|
||||
|
||||
YAW_LEFT,
|
||||
YAW_RIGHT,
|
||||
|
||||
PITCH_DOWN,
|
||||
PITCH_UP,
|
||||
|
||||
BOOM_IN,
|
||||
BOOM_OUT,
|
||||
|
||||
NUM_ACTIONS,
|
||||
};
|
||||
|
||||
float getActionState(Action action) const { return _actionStates[action]; }
|
||||
void assignDefaulActionScales();
|
||||
|
||||
// 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);
|
||||
|
||||
// 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;
|
||||
|
||||
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 hasModifier() { return _modifier.isValid(); }
|
||||
};
|
||||
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);
|
||||
//Grab all the input channels currently in use, return the number
|
||||
int getInputChannels(InputChannels& channels) const;
|
||||
|
||||
// Update means go grab all the device input channels and update the output channel values
|
||||
void update(float deltaTime);
|
||||
|
||||
// Default contruct allocate the poutput size with the current hardcoded action channels
|
||||
UserInputMapper() { assignDefaulActionScales(); }
|
||||
|
||||
protected:
|
||||
typedef std::map<int, DeviceProxy::Pointer> DevicesMap;
|
||||
DevicesMap _registeredDevices;
|
||||
uint16 _nextFreeDeviceID = 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> _actionScales = std::vector<float>(NUM_ACTIONS, 1.0f);
|
||||
};
|
||||
|
||||
#endif // hifi_UserInputMapper_h
|
|
@ -13,6 +13,7 @@
|
|||
#define hifi_TouchEvent_h
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <QTouchEvent>
|
||||
|
||||
class TouchEvent {
|
||||
public:
|
||||
|
|
Loading…
Reference in a new issue