// // 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 #include #include #include #include class UserInputMapper : public QObject { Q_OBJECT Q_ENUMS(Action) 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; } void setDevice(uint16 device) { _device = device; } void setChannel(uint16 channel) { _channel = channel; } void setType(uint16 type) { _type = type; } void setID(uint32 ID) { _id = ID; } bool isButton() const { return getType() == ChannelType::BUTTON; } bool isAxis() const { return getType() == ChannelType::AXIS; } bool isJoint() const { return getType() == ChannelType::JOINT; } // 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 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& right) const { return _id == right._id; } 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 ButtonGetter; typedef std::function AxisGetter; typedef std::function JointGetter; typedef QPair InputPair; typedef std::function ()> AvailableInputGetter; typedef std::function ResetBindings; typedef QVector AvailableInput; class DeviceProxy { public: DeviceProxy(QString name) { _name = name; } QString _name; 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(); }; AvailableInputGetter getAvailabeInputs = [] () -> AvailableInput { return QVector(); }; ResetBindings resetDeviceBindings = [] () -> bool { return true; }; typedef std::shared_ptr 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); QString getDeviceName(uint16 deviceID) { return _registeredDevices[deviceID]->_name; } QVector getAvailableInputs(uint16 deviceID) { return _registeredDevices[deviceID]->getAvailabeInputs(); } void resetAllDeviceBindings(); void resetDevice(uint16 deviceID); int findDevice(QString name); // 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, SHIFT, NUM_ACTIONS, }; std::vector _actionNames = std::vector(NUM_ACTIONS); void createActionNames(); QVector getAllActions(); QString getActionName(Action action) { return UserInputMapper::_actionNames[(int) action]; } 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; 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; } 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(); } }; 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 getAllInputsForDevice(uint16 device); QVector getInputChannelsForAction(UserInputMapper::Action action); std::multimap getActionToInputsMap() { return _actionToInputsMap; } // Update means go grab all the device input channels and update the output channel values void update(float deltaTime); UserInputMapper(); protected: typedef std::map DevicesMap; DevicesMap _registeredDevices; uint16 _nextFreeDeviceID = 1; typedef std::map InputToMoModifiersMap; InputToMoModifiersMap _inputToModifiersMap; typedef std::multimap ActionToInputsMap; ActionToInputsMap _actionToInputsMap; std::vector _actionStates = std::vector(NUM_ACTIONS, 0.0f); std::vector _actionScales = std::vector(NUM_ACTIONS, 1.0f); }; Q_DECLARE_METATYPE(UserInputMapper::InputPair) Q_DECLARE_METATYPE(QVector) Q_DECLARE_METATYPE(UserInputMapper::Input) Q_DECLARE_METATYPE(UserInputMapper::InputChannel) Q_DECLARE_METATYPE(QVector) Q_DECLARE_METATYPE(UserInputMapper::Action) Q_DECLARE_METATYPE(QVector) #endif // hifi_UserInputMapper_h