input plugin architecture, fixed sixense

This commit is contained in:
Anthony J. Thibault 2015-07-15 18:50:44 -07:00
parent fb62fda2d2
commit 56e7af081e
23 changed files with 344 additions and 326 deletions

View file

@ -2,7 +2,7 @@ set(TARGET_NAME interface)
project(${TARGET_NAME})
# set a default root dir for each of our optional externals if it was not passed
set(OPTIONAL_EXTERNALS "Faceshift" "LeapMotion" "RtMidi" "SDL2" "RSSDK")
set(OPTIONAL_EXTERNALS "Faceshift" "LeapMotion" "RtMidi" "RSSDK")
foreach(EXTERNAL ${OPTIONAL_EXTERNALS})
string(TOUPPER ${EXTERNAL} ${EXTERNAL}_UPPERCASE)
if (NOT ${${EXTERNAL}_UPPERCASE}_ROOT_DIR)

View file

@ -124,6 +124,7 @@
#include "devices/RealSense.h"
#include "devices/MIDIManager.h"
#include <input-plugins/SDL2Manager.h>
#include <input-plugins/SixenseManager.h>
#include <input-plugins/ViveControllerManager.h>
#include "RenderDeferredTask.h"
#include "scripting/AccountScriptingInterface.h"
@ -675,9 +676,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
connect(ddeTracker.data(), &FaceTracker::muteToggled, this, &Application::faceTrackerMuteToggled);
#endif
ViveControllerManager::getInstance().init();
if (ViveControllerManager::getInstance().isSupported()) {
ViveControllerManager::getInstance().activate();
ViveControllerManager::getInstance().init();
ViveControllerManager::getInstance().activate(NULL);
}
_oldHandMouseX[0] = -1;
@ -1640,10 +1641,10 @@ void Application::keyReleaseEvent(QKeyEvent* event) {
}
void Application::focusOutEvent(QFocusEvent* event) {
_keyboardMouseDevice.focusOutEvent(event);
SixenseManager::getInstance().focusOutEvent();
SDL2Manager::getInstance()->focusOutEvent();
ViveControllerManager::getInstance().focusOutEvent();
_keyboardMouseDevice.pluginFocusOutEvent();
SixenseManager::getInstance().pluginFocusOutEvent();
SDL2Manager::getInstance()->pluginFocusOutEvent();
ViveControllerManager::getInstance().pluginFocusOutEvent();
// synthesize events for keys currently pressed, since we may not get their release events
foreach (int key, _keysPressed) {
@ -2615,15 +2616,16 @@ void Application::update(float deltaTime) {
_lastFaceTrackerUpdate = 0;
}
SixenseManager::getInstance().update(deltaTime);
SDL2Manager::getInstance()->update();
ViveControllerManager::getInstance().update();
}
auto userInputMapper = DependencyManager::get<UserInputMapper>();
userInputMapper->setSensorToWorldMat(_myAvatar->getSensorToWorldMatrix());
userInputMapper->update(deltaTime);
_keyboardMouseDevice.update();
_keyboardMouseDevice.pluginUpdate(deltaTime);
SixenseManager::getInstance().pluginUpdate(deltaTime);
SDL2Manager::getInstance()->pluginUpdate(deltaTime);
ViveControllerManager::getInstance().pluginUpdate(deltaTime);
// Dispatch input events
_controllerScriptingInterface.updateInputControllers();
@ -3734,7 +3736,7 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
}
renderArgs->_debugFlags = renderDebugFlags;
_entities.render(renderArgs);
ViveControllerManager::getInstance().updateRendering(renderArgs, _main3DScene, pendingChanges);
//ViveControllerManager::getInstance().updateRendering(renderArgs, _main3DScene, pendingChanges);
}
// render the ambient occlusion effect if enabled

View file

@ -54,7 +54,6 @@
#include "Stars.h"
#include "avatar/Avatar.h"
#include "avatar/MyAvatar.h"
#include "devices/SixenseManager.h"
#include "scripting/ControllerScriptingInterface.h"
#include "scripting/WebWindowClass.h"
#include "ui/BandwidthDialog.h"

View file

@ -30,7 +30,7 @@
#include "devices/DdeFaceTracker.h"
#include "devices/Faceshift.h"
#include "devices/RealSense.h"
//#include "devices/SixenseManager.h"
#include <input-plugins\SixenseManager.h> // TODO: should be able to remove this once input plugin architecture is finished
#include "MainWindow.h"
#include "scripting/MenuScriptingInterface.h"
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)

View file

@ -11,7 +11,9 @@
//
#include "KeyboardMouseDevice.h"
void KeyboardMouseDevice::update() {
const QString KeyboardMouseDevice::NAME = "Keyboard";
void KeyboardMouseDevice::update(float deltaTime) {
_axisStateMap.clear();
// For touch event, we need to check that the last event is not too long ago
@ -27,7 +29,7 @@ void KeyboardMouseDevice::update() {
}
}
void KeyboardMouseDevice::focusOutEvent(QFocusEvent* event) {
void KeyboardMouseDevice::focusOutEvent() {
_buttonPressedMap.clear();
};
@ -159,7 +161,7 @@ void KeyboardMouseDevice::registerToUserInputMapper(UserInputMapper& mapper) {
// Grab the current free device ID
_deviceID = mapper.getFreeDeviceID();
auto proxy = UserInputMapper::DeviceProxy::Pointer(new UserInputMapper::DeviceProxy("Keyboard"));
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->getAvailabeInputs = [this] () -> QVector<UserInputMapper::InputPair> {
@ -282,23 +284,3 @@ void KeyboardMouseDevice::assignDefaultInputMapping(UserInputMapper& mapper) {
mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(Qt::Key_T));
}
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;
}
}

View file

@ -14,11 +14,12 @@
#include <QTouchEvent>
#include <chrono>
#include <input-plugins/UserInputMapper.h>
#include <input-plugins/InputDevice.h>
#include <input-plugins/InputPlugin.h>
class KeyboardMouseDevice {
class KeyboardMouseDevice : public InputPlugin, public InputDevice {
Q_OBJECT
public:
enum KeyboardChannel {
KEYBOARD_FIRST = 0,
KEYBOARD_LAST = 255,
@ -53,10 +54,27 @@ public:
TOUCH_BUTTON_PRESS = TOUCH_AXIS_Y_NEG + 1,
};
typedef std::unordered_set<int> ButtonPressedMap;
typedef std::map<int, float> AxisStateMap; // 8 axes
KeyboardMouseDevice() : InputDevice("Keyboard") {}
void focusOutEvent(QFocusEvent* event);
// Plugin functions
virtual bool isSupported() const override { return true; }
virtual bool isHandController() const override { return false; }
const QString& getName() const { return NAME; }
virtual void init() override {};
virtual void deinit() override {};
virtual void activate(PluginContainer * container) override {};
virtual void deactivate() override {};
virtual void idle() override {};
virtual void pluginFocusOutEvent() override { focusOutEvent(); }
virtual void pluginUpdate(float deltaTime) override { update(deltaTime); }
// Device functions
virtual void registerToUserInputMapper(UserInputMapper& mapper) override;
virtual void assignDefaultInputMapping(UserInputMapper& mapper) override;
virtual void update(float deltaTime) override;
virtual void focusOutEvent() override;
void keyPressEvent(QKeyEvent* event);
void keyReleaseEvent(QKeyEvent* event);
@ -70,32 +88,15 @@ public:
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;
@ -103,6 +104,9 @@ protected:
glm::vec2 evalAverageTouchPoints(const QList<QTouchEvent::TouchPoint>& points) const;
std::chrono::high_resolution_clock _clock;
std::chrono::high_resolution_clock::time_point _lastTouchTime;
private:
static const QString NAME;
};
#endif // hifi_KeyboardMouseDevice_h

View file

@ -16,9 +16,11 @@
#include "Application.h"
#include "devices/MotionTracker.h"
//#include "devices/SixenseManager.h"
#include "ControllerScriptingInterface.h"
// TODO: this needs to be removed, as well as any related controller-specific information
#include <input-plugins\SixenseManager.h>
ControllerScriptingInterface::ControllerScriptingInterface() :
_mouseCaptured(false),

View file

@ -22,6 +22,7 @@
#include "Tooltip.h"
#include "Application.h"
#include <input-plugins\SixenseManager.h> // TODO: any references to sixense should be removed here
// Used to animate the magnification windows

View file

@ -15,7 +15,6 @@
#include <AudioClient.h>
#include <avatar/AvatarManager.h>
#include <devices/Faceshift.h>
#include <devices/SixenseManager.h>
#include <NetworkingConstants.h>
#include "Application.h"

View file

@ -16,7 +16,7 @@
#include <avatar/AvatarManager.h>
#include <devices/DdeFaceTracker.h>
#include <devices/Faceshift.h>
#include <devices/SixenseManager.h>
#include <input-plugins/SixenseManager.h> // TODO: This should be replaced with InputDevice/InputPlugin, or something similar
#include <NetworkingConstants.h>
#include "Application.h"

View file

@ -1,5 +1,15 @@
set(TARGET_NAME input-plugins)
# set a default root dir for each of our optional externals if it was not passed
set(OPTIONAL_EXTERNALS "SDL2" "Sixense")
foreach(EXTERNAL ${OPTIONAL_EXTERNALS})
string(TOUPPER ${EXTERNAL} ${EXTERNAL}_UPPERCASE)
if (NOT ${${EXTERNAL}_UPPERCASE}_ROOT_DIR)
string(TOLOWER ${EXTERNAL} ${EXTERNAL}_LOWERCASE)
set(${${EXTERNAL}_UPPERCASE}_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/${${EXTERNAL}_LOWERCASE}")
endif ()
endforeach()
setup_hifi_library()
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
@ -16,7 +26,45 @@ find_package(OpenVR REQUIRED)
target_include_directories(${TARGET_NAME} PRIVATE ${OPENVR_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ${OPENVR_LIBRARIES})
add_dependency_external_projects(Sixense)
find_package(Sixense REQUIRED)
target_include_directories(${TARGET_NAME} PRIVATE ${SIXENSE_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ${SIXENSE_LIBRARIES})
#add_dependency_external_projects(Sixense)
#find_package(Sixense REQUIRED)
#target_include_directories(${TARGET_NAME} PRIVATE ${SIXENSE_INCLUDE_DIRS})
#target_link_libraries(${TARGET_NAME} ${SIXENSE_LIBRARIES})
# perform standard include and linking for found externals
foreach(EXTERNAL ${OPTIONAL_EXTERNALS})
if (${${EXTERNAL}_UPPERCASE}_REQUIRED)
find_package(${EXTERNAL} REQUIRED)
else ()
find_package(${EXTERNAL})
endif ()
if (${${EXTERNAL}_UPPERCASE}_FOUND AND NOT DISABLE_${${EXTERNAL}_UPPERCASE})
add_definitions(-DHAVE_${${EXTERNAL}_UPPERCASE})
# include the library directories (ignoring warnings)
if (NOT ${${EXTERNAL}_UPPERCASE}_INCLUDE_DIRS)
set(${${EXTERNAL}_UPPERCASE}_INCLUDE_DIRS ${${${EXTERNAL}_UPPERCASE}_INCLUDE_DIR})
endif ()
include_directories(SYSTEM ${${${EXTERNAL}_UPPERCASE}_INCLUDE_DIRS})
# perform the system include hack for OS X to ignore warnings
if (APPLE)
foreach(EXTERNAL_INCLUDE_DIR ${${${EXTERNAL}_UPPERCASE}_INCLUDE_DIRS})
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${EXTERNAL_INCLUDE_DIR}")
endforeach()
endif ()
if (NOT ${${EXTERNAL}_UPPERCASE}_LIBRARIES)
set(${${EXTERNAL}_UPPERCASE}_LIBRARIES ${${${EXTERNAL}_UPPERCASE}_LIBRARY})
endif ()
if (NOT APPLE OR NOT ${${EXTERNAL}_UPPERCASE} MATCHES "SIXENSE")
target_link_libraries(${TARGET_NAME} ${${${EXTERNAL}_UPPERCASE}_LIBRARIES})
elseif (APPLE AND NOT INSTALLER_BUILD)
add_definitions(-DSIXENSE_LIB_FILENAME=\"${${${EXTERNAL}_UPPERCASE}_LIBRARY_RELEASE}\")
endif ()
endif ()
endforeach()

View file

@ -0,0 +1,40 @@
//
// Created by Sam Gondelman on 7/15/2015
// 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 "InputDevice.h"
float InputDevice::getButton(int channel) const {
if (!_buttonPressedMap.empty()) {
if (_buttonPressedMap.find(channel) != _buttonPressedMap.end()) {
return 1.0f;
}
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;
}
else {
return 0.0f;
}
}
UserInputMapper::PoseValue InputDevice::getPose(int channel) const {
auto pose = _poseStateMap.find(channel);
if (pose != _poseStateMap.end()) {
return (*pose).second;
}
else {
return UserInputMapper::PoseValue();
}
}

View file

@ -0,0 +1,51 @@
//
// Created by Sam Gondelman on 7/15/2015
// 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
#include "UserInputMapper.h"
// Event types for each controller
const unsigned int CONTROLLER_0_EVENT = 1500U;
const unsigned int CONTROLLER_1_EVENT = 1501U;
// 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
class InputDevice {
public:
InputDevice::InputDevice(const QString& name) : _name(name) {}
typedef std::unordered_set<int> ButtonPressedMap;
typedef std::map<int, float> AxisStateMap;
typedef std::map<int, UserInputMapper::PoseValue> PoseStateMap;
// Get current state for each channel
float getButton(int channel) const;
float getAxis(int channel) const;
UserInputMapper::PoseValue getPose(int channel) const;
virtual void registerToUserInputMapper(UserInputMapper& mapper) = 0;
virtual void assignDefaultInputMapping(UserInputMapper& mapper) = 0;
// Update call MUST be called once per simulation loop
// It takes care of updating the action states and deltas
virtual void update(float deltaTime) = 0;
virtual void focusOutEvent() = 0;
int getDeviceID() { return _deviceID; }
protected:
int _deviceID = 0;
QString _name;
ButtonPressedMap _buttonPressedMap;
AxisStateMap _axisStateMap;
PoseStateMap _poseStateMap;
};

View file

@ -5,4 +5,4 @@
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "InputPlugin.h"
#include "InputPlugin.h"

View file

@ -9,9 +9,14 @@
#include <plugins/Plugin.h>
const float DEFAULT_HAND_RETICLE_MOVE_SPEED = 37.5f;
class InputPlugin : public Plugin {
Q_OBJECT
public:
virtual bool isHandController() = 0;
virtual bool isHandController() const = 0;
virtual void pluginFocusOutEvent() = 0;
virtual void pluginUpdate(float deltaTime) = 0;
};

View file

@ -21,6 +21,7 @@ const float CONTROLLER_THRESHOLD = 0.3f;
const float MAX_AXIS = 32768.0f;
Joystick::Joystick(SDL_JoystickID instanceId, const QString& name, SDL_GameController* sdlGameController) :
InputDevice(name),
_sdlGameController(sdlGameController),
_sdlJoystick(SDL_GameControllerGetJoystick(_sdlGameController)),
_instanceId(instanceId)
@ -40,7 +41,7 @@ void Joystick::closeJoystick() {
#endif
}
void Joystick::update() {
void Joystick::update(float deltaTime) {
for (auto axisState : _axisStateMap) {
if (fabsf(axisState.second) < CONTROLLER_THRESHOLD) {
_axisStateMap[axisState.first] = 0.0f;
@ -212,26 +213,6 @@ void Joystick::assignDefaultInputMapping(UserInputMapper& mapper) {
#endif
}
float Joystick::getButton(int channel) const {
if (!_buttonPressedMap.empty()) {
if (_buttonPressedMap.find(channel) != _buttonPressedMap.end()) {
return 1.0f;
} else {
return 0.0f;
}
}
return 0.0f;
}
float Joystick::getAxis(int channel) const {
auto axis = _axisStateMap.find(channel);
if (axis != _axisStateMap.end()) {
return (*axis).second;
} else {
return 0.0f;
}
}
#ifdef HAVE_SDL2
UserInputMapper::Input Joystick::makeInput(SDL_GameControllerButton button) {
return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON);

View file

@ -20,11 +20,10 @@
#undef main
#endif
#include "UserInputMapper.h"
#include "InputDevice.h"
class Joystick : public QObject {
class Joystick : public QObject, InputDevice {
Q_OBJECT
Q_PROPERTY(QString name READ getName)
#ifdef HAVE_SDL2
@ -44,27 +43,23 @@ public:
RIGHT_SHOULDER,
LEFT_SHOULDER,
};
const QString& getName() const { return _name; }
// Device functions
virtual void registerToUserInputMapper(UserInputMapper& mapper) override;
virtual void assignDefaultInputMapping(UserInputMapper& mapper) override;
virtual void update(float deltaTime) override;
virtual void focusOutEvent() override;
Joystick();
Joystick() : InputDevice("Joystick") {}
~Joystick();
typedef std::unordered_set<int> ButtonPressedMap;
typedef std::map<int, float> AxisStateMap;
float getButton(int channel) const;
float getAxis(int channel) const;
#ifdef HAVE_SDL2
UserInputMapper::Input makeInput(SDL_GameControllerButton button);
#endif
UserInputMapper::Input makeInput(Joystick::JoystickAxisChannel axis);
void registerToUserInputMapper(UserInputMapper& mapper);
void assignDefaultInputMapping(UserInputMapper& mapper);
void update();
void focusOutEvent();
#ifdef HAVE_SDL2
Joystick(SDL_JoystickID instanceId, const QString& name, SDL_GameController* sdlGameController);
#endif
@ -76,27 +71,16 @@ public:
void handleButtonEvent(const SDL_ControllerButtonEvent& event);
#endif
const QString& getName() const { return _name; }
#ifdef HAVE_SDL2
int getInstanceId() const { return _instanceId; }
#endif
int getDeviceID() { return _deviceID; }
private:
#ifdef HAVE_SDL2
SDL_GameController* _sdlGameController;
SDL_Joystick* _sdlJoystick;
SDL_JoystickID _instanceId;
#endif
QString _name;
protected:
int _deviceID = 0;
ButtonPressedMap _buttonPressedMap;
AxisStateMap _axisStateMap;
};
#endif // hifi_Joystick_h

View file

@ -17,6 +17,8 @@
#include "SDL2Manager.h"
const QString SDL2Manager::NAME = "SDL2";
#ifdef HAVE_SDL2
SDL_JoystickID SDL2Manager::getInstanceId(SDL_GameController* controller) {
SDL_Joystick* joystick = SDL_GameControllerGetJoystick(controller);
@ -72,7 +74,15 @@ SDL2Manager* SDL2Manager::getInstance() {
return &sharedInstance;
}
void SDL2Manager::focusOutEvent() {
bool SDL2Manager::isSupported() const {
#ifdef HAVE_SDL2
return true;
#else
return false;
#endif
}
void SDL2Manager::pluginFocusOutEvent() {
#ifdef HAVE_SDL2
for (auto joystick : _openJoysticks) {
joystick->focusOutEvent();
@ -80,12 +90,12 @@ void SDL2Manager::focusOutEvent() {
#endif
}
void SDL2Manager::update() {
void SDL2Manager::pluginUpdate(float deltaTime) {
#ifdef HAVE_SDL2
if (_isInitialized) {
auto userInputMapper = DependencyManager::get<UserInputMapper>();
for (auto joystick : _openJoysticks) {
joystick->update();
joystick->update(deltaTime);
}
PerformanceTimer perfTimer("SDL2Manager::update");

View file

@ -16,22 +16,33 @@
#include <SDL.h>
#endif
#include "InputPlugin.h"
#include "UserInputMapper.h"
#include "Joystick.h"
class SDL2Manager : public QObject {
class SDL2Manager : public InputPlugin {
Q_OBJECT
public:
SDL2Manager();
~SDL2Manager();
void focusOutEvent();
void update();
static SDL2Manager* getInstance();
// Plugin functions
virtual bool isSupported() const override;
virtual bool isHandController() const override { return false; }
const QString& getName() const { return NAME; }
virtual void init() override {};
virtual void deinit() override {};
virtual void activate(PluginContainer * container) override {};
virtual void deactivate() override {};
virtual void idle() override {};
virtual void pluginFocusOutEvent() override;
virtual void pluginUpdate(float deltaTime) override;
signals:
void joystickAdded(Joystick* joystick);
@ -70,12 +81,11 @@ private:
int buttonPressed() const { return SDL_PRESSED; }
int buttonRelease() const { return SDL_RELEASED; }
#endif
#ifdef HAVE_SDL2
QMap<SDL_JoystickID, Joystick*> _openJoysticks;
#endif
bool _isInitialized;
static const QString NAME;
};
#endif // hifi__SDL2Manager_h

View file

@ -11,13 +11,16 @@
#include <vector>
#include <avatar/AvatarManager.h>
#include <PerfStat.h>
#include "Application.h"
#include "NumericalConstants.h"
#include "SixenseManager.h"
#include "UserActivityLogger.h"
#include "InterfaceLogging.h"
// TODO: This should not be here
#include <QLoggingCategory>
Q_DECLARE_LOGGING_CATEGORY(inputplugins)
Q_LOGGING_CATEGORY(inputplugins, "hifi.inputplugins")
// These bits aren't used for buttons, so they can be used as masks:
const unsigned int LEFT_MASK = 0;
@ -46,12 +49,15 @@ typedef int (*SixenseTakeIntAndSixenseControllerData)(int, sixenseControllerData
#endif
const QString SixenseManager::NAME = "Sixense";
SixenseManager& SixenseManager::getInstance() {
static SixenseManager sharedInstance;
return sharedInstance;
}
SixenseManager::SixenseManager() :
InputDevice("Hydra"),
#if defined(HAVE_SIXENSE) && defined(__APPLE__)
_sixenseLibrary(NULL),
#endif
@ -80,7 +86,15 @@ SixenseManager::~SixenseManager() {
#endif
}
void SixenseManager::initialize() {
bool SixenseManager::isSupported() const {
#ifdef HAVE_SIXENSE
return true;
#else
return false;
#endif
}
void SixenseManager::init() {
#ifdef HAVE_SIXENSE
if (!_isInitialized) {
@ -144,18 +158,21 @@ void SixenseManager::setFilter(bool filter) {
void SixenseManager::update(float deltaTime) {
#ifdef HAVE_SIXENSE
Hand* hand = DependencyManager::get<AvatarManager>()->getMyAvatar()->getHand();
//Hand* hand = DependencyManager::get<AvatarManager>()->getMyAvatar()->getHand();
if (_isInitialized && _isEnabled) {
_buttonPressedMap.clear();
#ifdef __APPLE__
SixenseBaseFunction sixenseGetNumActiveControllers =
(SixenseBaseFunction) _sixenseLibrary->resolve("sixenseGetNumActiveControllers");
#endif
auto userInputMapper = DependencyManager::get<UserInputMapper>();
if (sixenseGetNumActiveControllers() == 0) {
_hydrasConnected = false;
if (_deviceID != 0) {
Application::getUserInputMapper()->removeDevice(_deviceID);
userInputMapper->removeDevice(_deviceID);
_deviceID = 0;
_poseStateMap.clear();
// if (_prevPalms[0]) {
@ -171,8 +188,8 @@ void SixenseManager::update(float deltaTime) {
PerformanceTimer perfTimer("sixense");
if (!_hydrasConnected) {
_hydrasConnected = true;
registerToUserInputMapper(*Application::getUserInputMapper());
getInstance().assignDefaultInputMapping(*Application::getUserInputMapper());
registerToUserInputMapper(*userInputMapper);
getInstance().assignDefaultInputMapping(*userInputMapper);
UserActivityLogger::getInstance().connectedDevice("spatial_controller", "hydra");
}
@ -240,7 +257,7 @@ void SixenseManager::update(float deltaTime) {
glm::quat rotation(data->rot_quat[3], -data->rot_quat[0], data->rot_quat[1], -data->rot_quat[2]);
rotation = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)) * _orbRotation * rotation;
handlePoseEvent(position, rotation, numActiveControlers - 1);
handlePoseEvent(position, rotation, numActiveControllers - 1);
} else {
_poseStateMap.clear();
}
@ -292,9 +309,9 @@ float SixenseManager::getCursorPixelRangeMult() const {
void SixenseManager::toggleSixense(bool shouldEnable) {
if (shouldEnable && !isInitialized()) {
initialize();
setFilter(Menu::getInstance()->isOptionChecked(MenuOption::FilterSixense));
setLowVelocityFilter(Menu::getInstance()->isOptionChecked(MenuOption::LowVelocityFilter));
init();
//setFilter(Menu::getInstance()->isOptionChecked(MenuOption::FilterSixense));
//setLowVelocityFilter(Menu::getInstance()->isOptionChecked(MenuOption::LowVelocityFilter));
}
setIsEnabled(shouldEnable);
}
@ -337,11 +354,11 @@ void SixenseManager::updateCalibration(const sixenseControllerData* controllers)
glm::vec3 zAxis = glm::normalize(glm::cross(xAxis, yAxis));
xAxis = glm::normalize(glm::cross(yAxis, zAxis));
_orbRotation = glm::inverse(glm::quat_cast(glm::mat3(xAxis, yAxis, zAxis)));
qCDebug(interfaceapp, "succeess: sixense calibration");
qCDebug(inputplugins, "succeess: sixense calibration");
}
break;
default:
qCDebug(interfaceapp, "failed: sixense calibration");
qCDebug(inputplugins, "failed: sixense calibration");
break;
}
@ -360,7 +377,7 @@ void SixenseManager::updateCalibration(const sixenseControllerData* controllers)
if (_calibrationState == CALIBRATION_STATE_IDLE) {
float reach = glm::distance(positionLeft, positionRight);
if (reach > 2.0f * MINIMUM_ARM_REACH) {
qCDebug(interfaceapp, "started: sixense calibration");
qCDebug(inputplugins, "started: sixense calibration");
_averageLeft = positionLeft;
_averageRight = positionRight;
_reachLeft = _averageLeft;
@ -393,7 +410,7 @@ void SixenseManager::updateCalibration(const sixenseControllerData* controllers)
_lastDistance = 0.0f;
_reachUp = 0.5f * (_reachLeft + _reachRight);
_calibrationState = CALIBRATION_STATE_Y;
qCDebug(interfaceapp, "success: sixense calibration: left");
qCDebug(inputplugins, "success: sixense calibration: left");
}
}
else if (_calibrationState == CALIBRATION_STATE_Y) {
@ -412,7 +429,7 @@ void SixenseManager::updateCalibration(const sixenseControllerData* controllers)
_lastDistance = 0.0f;
_lockExpiry = now + LOCK_DURATION;
_calibrationState = CALIBRATION_STATE_Z;
qCDebug(interfaceapp, "success: sixense calibration: up");
qCDebug(inputplugins, "success: sixense calibration: up");
}
}
}
@ -434,7 +451,7 @@ void SixenseManager::updateCalibration(const sixenseControllerData* controllers)
if (fabsf(_lastDistance) > 0.05f * MINIMUM_ARM_REACH) {
// lock has expired so clamp the data and move on
_calibrationState = CALIBRATION_STATE_COMPLETE;
qCDebug(interfaceapp, "success: sixense calibration: forward");
qCDebug(inputplugins, "success: sixense calibration: forward");
// TODO: it is theoretically possible to detect that the controllers have been
// accidentally switched (left hand is holding right controller) and to swap the order.
}
@ -547,7 +564,7 @@ void SixenseManager::registerToUserInputMapper(UserInputMapper& mapper) {
// Grab the current free device ID
_deviceID = mapper.getFreeDeviceID();
auto proxy = UserInputMapper::DeviceProxy::Pointer(new UserInputMapper::DeviceProxy("Hydra"));
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()); };
@ -633,35 +650,6 @@ void SixenseManager::assignDefaultInputMapping(UserInputMapper& mapper) {
}
float SixenseManager::getButton(int channel) const {
if (!_buttonPressedMap.empty()) {
if (_buttonPressedMap.find(channel) != _buttonPressedMap.end()) {
return 1.0f;
} else {
return 0.0f;
}
}
return 0.0f;
}
float SixenseManager::getAxis(int channel) const {
auto axis = _axisStateMap.find(channel);
if (axis != _axisStateMap.end()) {
return (*axis).second;
} else {
return 0.0f;
}
}
UserInputMapper::PoseValue SixenseManager::getPose(int channel) const {
auto pose = _poseStateMap.find(channel);
if (pose != _poseStateMap.end()) {
return (*pose).second;
} else {
return UserInputMapper::PoseValue();
}
}
UserInputMapper::Input SixenseManager::makeInput(unsigned int button, int index) {
return UserInputMapper::Input(_deviceID, button | (index == 0 ? LEFT_MASK : RIGHT_MASK), UserInputMapper::ChannelType::BUTTON);
}

View file

@ -12,9 +12,6 @@
#ifndef hifi_SixenseManager_h
#define hifi_SixenseManager_h
#include <QObject>
#include <unordered_set>
#ifdef HAVE_SIXENSE
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
@ -26,7 +23,8 @@
#endif
#include <input-plugins/UserInputMapper.h>
#include "InputPlugin.h"
#include "InputDevice.h"
const unsigned int BUTTON_0 = 1U << 0; // the skinny button between 1 and 2
const unsigned int BUTTON_1 = 1U << 5;
@ -36,15 +34,10 @@ const unsigned int BUTTON_4 = 1U << 4;
const unsigned int BUTTON_FWD = 1U << 7;
const unsigned int BUTTON_TRIGGER = 1U << 8;
// Event type that represents using the controller
const unsigned int CONTROLLER_0_EVENT = 1500U;
const unsigned int CONTROLLER_1_EVENT = 1501U;
const float DEFAULT_SIXENSE_RETICLE_MOVE_SPEED = 37.5f;
const bool DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS = false;
/// Handles interaction with the Sixense SDK (e.g., Razer Hydra).
class SixenseManager : public QObject {
// Handles interaction with the Sixense SDK (e.g., Razer Hydra).
class SixenseManager : public InputPlugin, public InputDevice {
Q_OBJECT
public:
enum JoystickAxisChannel {
@ -61,13 +54,31 @@ public:
};
static SixenseManager& getInstance();
void initialize();
// Plugin functions
virtual bool isSupported() const override;
virtual bool isHandController() const override { return false; }
const QString& getName() const { return NAME; }
virtual void init() override;
virtual void deinit() override {};
virtual void activate(PluginContainer * container) override {};
virtual void deactivate() override {};
virtual void idle() override {};
virtual void pluginFocusOutEvent() override { focusOutEvent(); }
virtual void pluginUpdate(float deltaTime) override { update(deltaTime); }
// Device functions
virtual void registerToUserInputMapper(UserInputMapper& mapper) override;
virtual void assignDefaultInputMapping(UserInputMapper& mapper) override;
virtual void update(float deltaTime) override;
virtual void focusOutEvent() override;
bool isInitialized() const { return _isInitialized; }
void setIsEnabled(bool isEnabled) { _isEnabled = isEnabled; }
void update(float deltaTime);
float getCursorPixelRangeMult() const;
float getReticleMoveSpeed() const { return _reticleMoveSpeed; }
@ -75,23 +86,10 @@ public:
bool getInvertButtons() const { return _invertButtons; }
void setInvertButtons(bool invertSixenseButtons) { _invertButtons = invertSixenseButtons; }
typedef std::unordered_set<int> ButtonPressedMap;
typedef std::map<int, float> AxisStateMap;
typedef std::map<int, UserInputMapper::PoseValue> PoseStateMap;
float getButton(int channel) const;
float getAxis(int channel) const;
UserInputMapper::PoseValue getPose(int channel) const;
UserInputMapper::Input makeInput(unsigned int button, int index);
UserInputMapper::Input makeInput(JoystickAxisChannel axis, int index);
UserInputMapper::Input makeInput(JointChannel joint);
void registerToUserInputMapper(UserInputMapper& mapper);
void assignDefaultInputMapping(UserInputMapper& mapper);
void focusOutEvent();
public slots:
void toggleSixense(bool shouldEnable);
void setFilter(bool filter);
@ -137,15 +135,10 @@ private:
bool _lowVelocityFilter;
bool _controllersAtBase;
float _reticleMoveSpeed = DEFAULT_SIXENSE_RETICLE_MOVE_SPEED;
float _reticleMoveSpeed = DEFAULT_HAND_RETICLE_MOVE_SPEED;
bool _invertButtons = DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS;
protected:
int _deviceID = 0;
ButtonPressedMap _buttonPressedMap;
AxisStateMap _axisStateMap;
PoseStateMap _poseStateMap;
static const QString NAME;
};
#endif // hifi_SixenseManager_h

View file

@ -21,12 +21,6 @@
#include "NumericalConstants.h"
#include "UserActivityLogger.h"
#include <QLoggingCategory>
Q_DECLARE_LOGGING_CATEGORY(inputplugins)
Q_LOGGING_CATEGORY(inputplugins, "hifi.inputplugins")
const QString ViveControllerManager::NAME("OpenVR (Vive) Hand Controllers");
extern vr::IVRSystem *_hmd;
extern vr::TrackedDevicePose_t _trackedDevicePose[vr::k_unMaxTrackedDeviceCount];
extern mat4 _trackedDevicePoseMat4[vr::k_unMaxTrackedDeviceCount];
@ -47,12 +41,15 @@ const unsigned int TRIGGER_BUTTON = 1U << 4;
const float CONTROLLER_LENGTH_OFFSET = 0.175f;
const QString CONTROLLER_MODEL_STRING = "vr_controller_05_wireless_b";
const QString ViveControllerManager::NAME = "OpenVR";
ViveControllerManager& ViveControllerManager::getInstance() {
static ViveControllerManager sharedInstance;
return sharedInstance;
}
ViveControllerManager::ViveControllerManager() :
InputDevice("SteamVR Controller"),
_isInitialized(false),
_isEnabled(true),
_trackedControllers(0),
@ -67,41 +64,15 @@ ViveControllerManager::~ViveControllerManager() {
}
const QString& ViveControllerManager::getName() const {
return NAME;
}
bool ViveControllerManager::isSupported() const {
return vr::VR_IsHmdPresent();
}
void ViveControllerManager::init() {
;
}
void ViveControllerManager::deinit() {
;
// TODO: deinit openvr? avoid conflicts with display plugin
}
void ViveControllerManager::activate(PluginContainer * container) {
activate();
}
/// Called when a plugin is no longer being used. May be called multiple times.
void ViveControllerManager::deactivate() {
;
}
/**
* Called by the application during it's idle phase. If the plugin needs to do
* CPU intensive work, it should launch a thread for that, rather than trying to
* do long operations in the idle call
*/
void ViveControllerManager::idle() {
update();
}
void ViveControllerManager::activate() {
void ViveControllerManager::init() {
if (!_hmd) {
vr::HmdError eError = vr::HmdError_None;
_hmd = vr::VR_Init(&eError);
@ -220,7 +191,7 @@ void ViveControllerManager::renderHand(UserInputMapper::PoseValue pose, gpu::Bat
batch.drawIndexed(gpu::TRIANGLES, mesh->getNumIndices(), 0);
}
void ViveControllerManager::update() {
void ViveControllerManager::update(float deltaTime) {
if (!_hmd) {
return;
}
@ -333,7 +304,7 @@ void ViveControllerManager::registerToUserInputMapper(UserInputMapper& mapper) {
// Grab the current free device ID
_deviceID = mapper.getFreeDeviceID();
auto proxy = UserInputMapper::DeviceProxy::Pointer(new UserInputMapper::DeviceProxy("SteamVR Controller"));
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()); };
@ -405,35 +376,6 @@ void ViveControllerManager::assignDefaultInputMapping(UserInputMapper& mapper) {
mapper.addInputChannel(UserInputMapper::RIGHT_HAND, makeInput(RIGHT_HAND));
}
float ViveControllerManager::getButton(int channel) const {
if (!_buttonPressedMap.empty()) {
if (_buttonPressedMap.find(channel) != _buttonPressedMap.end()) {
return 1.0f;
} else {
return 0.0f;
}
}
return 0.0f;
}
float ViveControllerManager::getAxis(int channel) const {
auto axis = _axisStateMap.find(channel);
if (axis != _axisStateMap.end()) {
return (*axis).second;
} else {
return 0.0f;
}
}
UserInputMapper::PoseValue ViveControllerManager::getPose(int channel) const {
auto pose = _poseStateMap.find(channel);
if (pose != _poseStateMap.end()) {
return (*pose).second;
} else {
return UserInputMapper::PoseValue();
}
}
UserInputMapper::Input ViveControllerManager::makeInput(unsigned int button, int index) {
return UserInputMapper::Input(_deviceID, button | (index == 0 ? LEFT_MASK : RIGHT_MASK), UserInputMapper::ChannelType::BUTTON);
}

View file

@ -19,34 +19,14 @@
#include <model/Geometry.h>
#include <gpu/Texture.h>
#include "InputDevice.h"
#include "InputPlugin.h"
#include <RenderArgs.h>
#include <render/Scene.h>
#include "UserInputMapper.h"
class ViveControllerManager : public InputPlugin {
class ViveControllerManager : public InputPlugin, public InputDevice {
Q_OBJECT
public:
virtual const QString& getName() const override;
virtual bool isSupported() const override;
virtual bool isHandController() { return true; }
/// Called when plugin is initially loaded, typically at application start
virtual void init() override;
/// Called when application is shutting down
virtual void deinit() override ;
/// Called when a plugin is being activated for use. May be called multiple times.
virtual void activate(PluginContainer * container) override;
/// Called when a plugin is no longer being used. May be called multiple times.
virtual void deactivate() override;
/**
* Called by the application during it's idle phase. If the plugin needs to do
* CPU intensive work, it should launch a thread for that, rather than trying to
* do long operations in the idle call
*/
virtual void idle() override;
enum JoystickAxisChannel {
AXIS_Y_POS = 1U << 1,
AXIS_Y_NEG = 1U << 2,
@ -67,31 +47,35 @@ public:
LEFT_HAND = 0,
RIGHT_HAND,
};
void focusOutEvent();
static ViveControllerManager& getInstance();
// Plugin functions
virtual bool isSupported() const override;
virtual bool isHandController() const override { return true; }
const QString& getName() const { return NAME; }
virtual void init() override;
virtual void deinit() override;
virtual void activate(PluginContainer * container) override {};
virtual void deactivate() override {};
virtual void idle() override {};
virtual void pluginFocusOutEvent() override { focusOutEvent(); }
virtual void pluginUpdate(float deltaTime) override { update(deltaTime); }
// Device functions
virtual void registerToUserInputMapper(UserInputMapper& mapper) override;
virtual void assignDefaultInputMapping(UserInputMapper& mapper) override;
virtual void update(float deltaTime) override;
virtual void focusOutEvent() override;
void updateRendering(RenderArgs* args, render::ScenePointer scene, render::PendingChanges pendingChanges);
void update();
static ViveControllerManager& getInstance();
typedef std::unordered_set<int> ButtonPressedMap;
typedef std::map<int, float> AxisStateMap;
typedef std::map<int, UserInputMapper::PoseValue> PoseStateMap;
float getButton(int channel) const;
float getAxis(int channel) const;
UserInputMapper::PoseValue getPose(int channel) const;
UserInputMapper::Input makeInput(unsigned int button, int index);
UserInputMapper::Input makeInput(JoystickAxisChannel axis, int index);
UserInputMapper::Input makeInput(JointChannel joint);
void registerToUserInputMapper(UserInputMapper& mapper);
void assignDefaultInputMapping(UserInputMapper& mapper);
void activate();
private:
ViveControllerManager();
~ViveControllerManager();
@ -114,13 +98,6 @@ private:
int _rightHandRenderID;
static const QString NAME;
protected:
int _deviceID = 0;
ButtonPressedMap _buttonPressedMap;
AxisStateMap _axisStateMap;
PoseStateMap _poseStateMap;
};