mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-08 19:23:28 +02:00
Working on refactoring the xbox hardware access and wiring up test code
This commit is contained in:
parent
d29c7ef267
commit
14f511350d
38 changed files with 1815 additions and 1172 deletions
|
@ -8,84 +8,6 @@
|
|||
|
||||
#include "Endpoint.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <input-plugins/UserInputMapper.h>
|
||||
namespace controller {
|
||||
|
||||
namespace Controllers {
|
||||
|
||||
// Ex: xbox.RY, xbox.A ....
|
||||
class HardwareEndpoint : public Endpoint {
|
||||
public:
|
||||
virtual float value() override {
|
||||
// ...
|
||||
}
|
||||
|
||||
virtual void apply(float newValue, float oldValue, const Endpoint& source) override {
|
||||
// Default does nothing, but in theory this could be something like vibration
|
||||
// mapping.from(xbox.X).to(xbox.Vibrate)
|
||||
}
|
||||
};
|
||||
|
||||
// Ex: Standard.RY, Action.Yaw
|
||||
class VirtualEndpoint : public Endpoint {
|
||||
public:
|
||||
virtual void apply(float newValue) {
|
||||
if (newValue != _lastValue) {
|
||||
_lastValue = newValue;
|
||||
}
|
||||
}
|
||||
|
||||
virtual float value() {
|
||||
return _lastValue;
|
||||
}
|
||||
|
||||
float _lastValue;
|
||||
};
|
||||
|
||||
float currentTime() {
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* A function which provides input
|
||||
*/
|
||||
class FunctionEndpoint : public Endpoint {
|
||||
public:
|
||||
|
||||
virtual float value() override {
|
||||
float now = currentTime();
|
||||
float delta = now - _lastCalled;
|
||||
float result = _inputFunction.call(_object, QScriptValue(delta)).toNumber();
|
||||
_lastCalled = now;
|
||||
return result;
|
||||
}
|
||||
|
||||
virtual void apply(float newValue, float oldValue, const Endpoint& source) override {
|
||||
if (newValue != oldValue) {
|
||||
//_outputFunction.call(newValue, oldValue, source);
|
||||
}
|
||||
}
|
||||
|
||||
float _lastValue{ NAN };
|
||||
float _lastCalled{ 0 };
|
||||
QScriptValue _outputFunction;
|
||||
QScriptValue _inputFunction;
|
||||
QScriptValue _object;
|
||||
};
|
||||
|
||||
|
||||
|
||||
// FIXME how do we handle dynamic changes in connected hardware?
|
||||
const Endpoint::List& Endpoint::getHardwareEndpoints() {
|
||||
static Endpoint::List ACTIVE_HARDWARE_ENDPOINTS;
|
||||
static std::once_flag once;
|
||||
std::call_once(once, [&] {
|
||||
auto userInputMapper = DependencyManager::get<UserInputMapper>();
|
||||
// TODO populate ACTIVE_HARDWARE with all the connected devices
|
||||
// For each connected device
|
||||
// for each input channel
|
||||
// build a HardwareEndpoint instance around the input channel and add it to the list
|
||||
});
|
||||
|
||||
return ACTIVE_HARDWARE_ENDPOINTS;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,26 +12,45 @@
|
|||
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
|
||||
#include <input-plugins/UserInputMapper.h>
|
||||
|
||||
class QScriptValue;
|
||||
|
||||
namespace Controllers {
|
||||
namespace controller {
|
||||
/*
|
||||
* Encapsulates a particular input / output,
|
||||
* i.e. Hydra.Button0, Standard.X, Action.Yaw
|
||||
*/
|
||||
class Endpoint {
|
||||
public:
|
||||
virtual float value() { return 0; } // = 0;
|
||||
virtual void apply(float newValue, float oldValue, const Endpoint& source) {} // = 0;
|
||||
|
||||
using Pointer = std::shared_ptr<Endpoint>;
|
||||
using List = std::list<Pointer>;
|
||||
using Pair = std::pair<Pointer, Pointer>;
|
||||
using ReadLambda = std::function<float()>;
|
||||
using WriteLambda = std::function<void(float)>;
|
||||
|
||||
static const List& getHardwareEndpoints();
|
||||
static Pointer getEndpoint(const QScriptValue& value);
|
||||
Endpoint(const UserInputMapper::Input& id) : _id(id) {}
|
||||
virtual float value() = 0;
|
||||
virtual void apply(float newValue, float oldValue, const Pointer& source) = 0;
|
||||
const UserInputMapper::Input& getId() { return _id; }
|
||||
protected:
|
||||
UserInputMapper::Input _id;
|
||||
};
|
||||
|
||||
class LambdaEndpoint : public Endpoint {
|
||||
public:
|
||||
LambdaEndpoint(ReadLambda readLambda, WriteLambda writeLambda = [](float) {})
|
||||
: Endpoint(UserInputMapper::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); }
|
||||
|
||||
private:
|
||||
ReadLambda _readLambda;
|
||||
WriteLambda _writeLambda;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#include <QtCore/QObject>
|
||||
#include <QtScript/QScriptValue>
|
||||
|
||||
namespace Controllers {
|
||||
namespace controller {
|
||||
|
||||
Filter::Pointer Filter::parse(const QJsonObject& json) {
|
||||
// FIXME parse the json object and determine the instance type to create
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
class QJsonObject;
|
||||
|
||||
namespace Controllers {
|
||||
namespace controller {
|
||||
|
||||
// Encapsulates part of a filter chain
|
||||
class Filter {
|
||||
|
|
11
libraries/controllers/src/controllers/Logging.cpp
Normal file
11
libraries/controllers/src/controllers/Logging.cpp
Normal file
|
@ -0,0 +1,11 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis 2015/10/11
|
||||
// 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 "Logging.h"
|
||||
|
||||
Q_LOGGING_CATEGORY(controllers, "hifi.controllers")
|
16
libraries/controllers/src/controllers/Logging.h
Normal file
16
libraries/controllers/src/controllers/Logging.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis 2015/10/11
|
||||
// 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_Controllers_Logging_h
|
||||
#define hifi_Controllers_Logging_h
|
||||
|
||||
#include <QLoggingCategory>
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(controllers)
|
||||
|
||||
#endif
|
|
@ -1,63 +1,5 @@
|
|||
#include "Mapping.h"
|
||||
|
||||
namespace Controllers {
|
||||
namespace controller {
|
||||
}
|
||||
|
||||
// class MappingsStack {
|
||||
// std::list<Mapping> _stack;
|
||||
// ValueMap _lastValues;
|
||||
//
|
||||
// void update() {
|
||||
// EndpointList hardwareInputs = getHardwareEndpoints();
|
||||
// ValueMap currentValues;
|
||||
//
|
||||
// for (auto input : hardwareInputs) {
|
||||
// currentValues[input] = input->value();
|
||||
// }
|
||||
//
|
||||
// // Now process the current values for each level of the stack
|
||||
// for (auto& mapping : _stack) {
|
||||
// update(mapping, currentValues);
|
||||
// }
|
||||
//
|
||||
// _lastValues = currentValues;
|
||||
// }
|
||||
//
|
||||
// void update(Mapping& mapping, ValueMap& values) {
|
||||
// ValueMap updates;
|
||||
// EndpointList consumedEndpoints;
|
||||
// for (const auto& entry : values) {
|
||||
// Endpoint* endpoint = entry.first;
|
||||
// if (!mapping._channelMappings.count(endpoint)) {
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// const Mapping::List& routes = mapping._channelMappings[endpoint];
|
||||
// consumedEndpoints.push_back(endpoint);
|
||||
// for (const auto& route : routes) {
|
||||
// float lastValue = 0;
|
||||
// if (mapping._lastValues.count(endpoint)) {
|
||||
// lastValue = mapping._lastValues[endpoint];
|
||||
// }
|
||||
// float value = entry.second;
|
||||
// for (const auto& filter : route._filters) {
|
||||
// value = filter->apply(value, lastValue);
|
||||
// }
|
||||
// updates[route._destination] = value;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // Update the last seen values
|
||||
// mapping._lastValues = values;
|
||||
//
|
||||
// // Remove all the consumed inputs
|
||||
// for (auto endpoint : consumedEndpoints) {
|
||||
// values.erase(endpoint);
|
||||
// }
|
||||
//
|
||||
// // Add all the updates (may restore some of the consumed data if a passthrough was created (i.e. source == dest)
|
||||
// for (const auto& entry : updates) {
|
||||
// values[entry.first] = entry.second;
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#define hifi_Controllers_Mapping_h
|
||||
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <QtCore/QString>
|
||||
|
||||
|
@ -18,19 +19,15 @@
|
|||
#include "Filter.h"
|
||||
#include "Route.h"
|
||||
|
||||
namespace Controllers {
|
||||
|
||||
using ValueMap = std::map<Endpoint::Pointer, float>;
|
||||
namespace controller {
|
||||
|
||||
class Mapping {
|
||||
public:
|
||||
// Map of source channels to route lists
|
||||
using Map = std::map<Endpoint::Pointer, Route::List>;
|
||||
using Pointer = std::shared_ptr<Mapping>;
|
||||
using List = std::list<Pointer>;
|
||||
|
||||
Map _channelMappings;
|
||||
ValueMap _lastValues;
|
||||
|
||||
void parse(const QString& json);
|
||||
QString serialize();
|
||||
|
@ -38,4 +35,4 @@ namespace Controllers {
|
|||
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -1,100 +1,330 @@
|
|||
#include "NewControllerScriptingInterface.h"
|
||||
//
|
||||
// Created by Bradley Austin Davis 2015/10/09
|
||||
// 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 "NewControllerScriptingInterface.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
|
||||
#include <QtCore/QRegularExpression>
|
||||
|
||||
#include <GLMHelpers.h>
|
||||
#include <DependencyManager.h>
|
||||
#include <input-plugins/UserInputMapper.h>
|
||||
#include <input-plugins/InputPlugin.h>
|
||||
#include <input-plugins/KeyboardMouseDevice.h>
|
||||
#include <plugins/PluginManager.h>
|
||||
|
||||
#include "impl/MappingBuilderProxy.h"
|
||||
#include "Logging.h"
|
||||
|
||||
namespace Controllers {
|
||||
void NewControllerScriptingInterface::update() {
|
||||
auto userInputMapper = DependencyManager::get<UserInputMapper>();
|
||||
static float last = secTimestampNow();
|
||||
float now = secTimestampNow();
|
||||
userInputMapper->update(now - last);
|
||||
last = now;
|
||||
static const uint16_t ACTIONS_DEVICE = UserInputMapper::Input::INVALID_DEVICE - (uint16_t)1;
|
||||
|
||||
namespace controller {
|
||||
|
||||
|
||||
class VirtualEndpoint : public Endpoint {
|
||||
public:
|
||||
VirtualEndpoint(const UserInputMapper::Input& id = UserInputMapper::Input(-1))
|
||||
: 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(-1)), _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;
|
||||
};
|
||||
|
||||
class CompositeEndpoint : public Endpoint, Endpoint::Pair {
|
||||
public:
|
||||
CompositeEndpoint(Endpoint::Pointer first, Endpoint::Pointer second)
|
||||
: Endpoint(UserInputMapper::Input(-1)), Pair(first, second) { }
|
||||
|
||||
virtual float value() {
|
||||
float result = first->value() * -1.0 + 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;
|
||||
};
|
||||
|
||||
QString sanatizeName(const QString& name) {
|
||||
QString cleanName{ name };
|
||||
cleanName.remove(QRegularExpression{ "[\\(\\)\\.\\s]" });
|
||||
return cleanName;
|
||||
}
|
||||
|
||||
QObject* NewControllerScriptingInterface::newMapping() {
|
||||
qDebug() << "Creating new Mapping proxy";
|
||||
return new MappingBuilderProxy(std::make_shared<Mapping>());
|
||||
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 = sanatizeName(inputMapping.second);
|
||||
qCDebug(controllers) << "\tInput " << input.getChannel() << (int)input.getType()
|
||||
<< QString::number(input.getID(), 16) << ": " << inputName;
|
||||
deviceMap.insert(inputName, input.getID());
|
||||
}
|
||||
return deviceMap;
|
||||
}
|
||||
|
||||
NewControllerScriptingInterface::NewControllerScriptingInterface() {
|
||||
auto userInputMapper = DependencyManager::get<UserInputMapper>();
|
||||
auto devices = userInputMapper->getDevices();
|
||||
for (const auto& deviceMapping : devices) {
|
||||
auto device = deviceMapping.second.get();
|
||||
auto deviceName = sanatizeName(device->getName());
|
||||
qCDebug(controllers) << "Device" << deviceMapping.first << ":" << deviceName;
|
||||
// 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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
auto actionNames = userInputMapper->getActionNames();
|
||||
int actionNumber = 0;
|
||||
qCDebug(controllers) << "Setting up standard actions";
|
||||
for (const auto& actionName : actionNames) {
|
||||
UserInputMapper::Input actionInput(ACTIONS_DEVICE, actionNumber++, UserInputMapper::ChannelType::AXIS);
|
||||
qCDebug(controllers) << "\tAction: " << actionName << " " << QString::number(actionInput.getID(), 16);
|
||||
// Expose the IDs to JS
|
||||
_actions.insert(sanatizeName(actionName), actionInput.getID());
|
||||
|
||||
// Create the endpoints
|
||||
// FIXME action endpoints need to accumulate values, and have them cleared at each frame
|
||||
_endpoints[actionInput] = std::make_shared<VirtualEndpoint>();
|
||||
}
|
||||
}
|
||||
|
||||
QObject* NewControllerScriptingInterface::newMapping(const QString& mappingName) {
|
||||
if (_mappingsByName.count(mappingName)) {
|
||||
qCWarning(controllers) << "Refusing to recreate mapping named " << mappingName;
|
||||
}
|
||||
qDebug() << "Creating new Mapping " << mappingName;
|
||||
Mapping::Pointer mapping = std::make_shared<Mapping>();
|
||||
_mappingsByName[mappingName] = mapping;
|
||||
return new MappingBuilderProxy(*this, mapping);
|
||||
}
|
||||
|
||||
void NewControllerScriptingInterface::enableMapping(const QString& mappingName, bool enable) {
|
||||
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 NewControllerScriptingInterface::getValue(const int& source) {
|
||||
//UserInputMapper::Input input; input._id = source;
|
||||
//auto userInputMapper = DependencyManager::get<UserInputMapper>();
|
||||
//auto deviceProxy = userInputMapper->getDeviceProxy(input);
|
||||
//return deviceProxy->getButton(input, 0) ? 1.0 : 0.0;
|
||||
// return (sin(secTimestampNow()) + 1.0f) / 2.0f;
|
||||
UserInputMapper::Input input(source);
|
||||
auto iterator = _endpoints.find(input);
|
||||
if (_endpoints.end() == iterator) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return (sin(secTimestampNow()) + 1.0f) / 2.0f;
|
||||
const auto& endpoint = iterator->second;
|
||||
return getValue(endpoint);
|
||||
}
|
||||
|
||||
float NewControllerScriptingInterface::getValue(const Endpoint::Pointer& endpoint) {
|
||||
auto valuesIterator = _overrideValues.find(endpoint);
|
||||
if (_overrideValues.end() != valuesIterator) {
|
||||
return valuesIterator->second;
|
||||
}
|
||||
|
||||
return endpoint->value();
|
||||
}
|
||||
|
||||
|
||||
void NewControllerScriptingInterface::update() {
|
||||
static float last = secTimestampNow();
|
||||
float now = secTimestampNow();
|
||||
float delta = now - last;
|
||||
last = now;
|
||||
|
||||
foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) {
|
||||
inputPlugin->pluginUpdate(delta, false);
|
||||
}
|
||||
|
||||
auto userInputMapper = DependencyManager::get<UserInputMapper>();
|
||||
userInputMapper->update(delta);
|
||||
|
||||
_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;
|
||||
|
||||
// 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;
|
||||
|
||||
if (writtenEndpoints.count(destination)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Standard controller destinations can only be can only be used once.
|
||||
if (userInputMapper->getStandardDeviceID() == destination->getId().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 NewControllerScriptingInterface::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 NewControllerScriptingInterface::endpointFor(const QScriptValue& endpoint) {
|
||||
if (endpoint.isNumber()) {
|
||||
return endpointFor(UserInputMapper::Input(endpoint.toInt32()));
|
||||
}
|
||||
|
||||
qWarning() << "Unsupported input type " << endpoint.toString();
|
||||
return Endpoint::Pointer();
|
||||
}
|
||||
|
||||
Endpoint::Pointer NewControllerScriptingInterface::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 NewControllerScriptingInterface::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;
|
||||
}
|
||||
|
||||
} // namespace controllers
|
||||
|
||||
|
||||
|
||||
|
||||
// class MappingsStack {
|
||||
// std::list<Mapping> _stack;
|
||||
// ValueMap _lastValues;
|
||||
//
|
||||
// void update() {
|
||||
// EndpointList hardwareInputs = getHardwareEndpoints();
|
||||
// ValueMap currentValues;
|
||||
//
|
||||
// for (auto input : hardwareInputs) {
|
||||
// currentValues[input] = input->value();
|
||||
// }
|
||||
//
|
||||
// // Now process the current values for each level of the stack
|
||||
// for (auto& mapping : _stack) {
|
||||
// update(mapping, currentValues);
|
||||
// }
|
||||
//
|
||||
// _lastValues = currentValues;
|
||||
// }
|
||||
//
|
||||
// void update(Mapping& mapping, ValueMap& values) {
|
||||
// ValueMap updates;
|
||||
// EndpointList consumedEndpoints;
|
||||
// for (const auto& entry : values) {
|
||||
// Endpoint* endpoint = entry.first;
|
||||
// if (!mapping._channelMappings.count(endpoint)) {
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// const Mapping::List& routes = mapping._channelMappings[endpoint];
|
||||
// consumedEndpoints.push_back(endpoint);
|
||||
// for (const auto& route : routes) {
|
||||
// float lastValue = 0;
|
||||
// if (mapping._lastValues.count(endpoint)) {
|
||||
// lastValue = mapping._lastValues[endpoint];
|
||||
// }
|
||||
// float value = entry.second;
|
||||
// for (const auto& filter : route._filters) {
|
||||
// value = filter->apply(value, lastValue);
|
||||
// }
|
||||
// updates[route._destination] = value;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // Update the last seen values
|
||||
// mapping._lastValues = values;
|
||||
//
|
||||
// // Remove all the consumed inputs
|
||||
// for (auto endpoint : consumedEndpoints) {
|
||||
// values.erase(endpoint);
|
||||
// }
|
||||
//
|
||||
// // Add all the updates (may restore some of the consumed data if a passthrough was created (i.e. source == dest)
|
||||
// for (const auto& entry : updates) {
|
||||
// values[entry.first] = entry.second;
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
//var mapping = Controller.newMapping();
|
||||
//mapping.map(hydra.LeftButton0, actions.ContextMenu);
|
||||
//mapping.map(hydra.LeftButton0).to(xbox.RT);
|
||||
|
@ -134,5 +364,3 @@ namespace Controllers {
|
|||
|
||||
// mappingSnap.from(hydra.LX).to(function(newValue, oldValue) {
|
||||
// timeSinceLastYaw += deltaTime
|
||||
|
||||
#include "NewControllerScriptingInterface.moc"
|
||||
|
|
|
@ -10,21 +10,78 @@
|
|||
#ifndef hifi_Controllers_NewControllerScriptingInterface_h
|
||||
#define hifi_Controllers_NewControllerScriptingInterface_h
|
||||
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QVariant>
|
||||
|
||||
#include <QtQml/QJSValue>
|
||||
#include <QtScript/QScriptValue>
|
||||
|
||||
#include <input-plugins/UserInputMapper.h>
|
||||
|
||||
#include "Mapping.h"
|
||||
|
||||
class QScriptValue;
|
||||
|
||||
namespace Controllers {
|
||||
namespace controller {
|
||||
class NewControllerScriptingInterface : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QVariantMap Hardware READ getHardware CONSTANT FINAL)
|
||||
Q_PROPERTY(QVariantMap Actions READ getActions CONSTANT FINAL)
|
||||
Q_PROPERTY(QVariantMap Standard READ getStandard CONSTANT FINAL)
|
||||
|
||||
public:
|
||||
Q_INVOKABLE void update();
|
||||
Q_INVOKABLE QObject* newMapping();
|
||||
NewControllerScriptingInterface();
|
||||
Q_INVOKABLE float getValue(const int& source);
|
||||
|
||||
Q_INVOKABLE void update();
|
||||
Q_INVOKABLE QObject* newMapping(const QString& mappingName);
|
||||
Q_INVOKABLE void enableMapping(const QString& mappingName, bool enable = true);
|
||||
Q_INVOKABLE void disableMapping(const QString& mappingName) {
|
||||
enableMapping(mappingName, false);
|
||||
}
|
||||
|
||||
|
||||
const QVariantMap& getHardware() { return _hardware; }
|
||||
const QVariantMap& getActions() { return _actions; }
|
||||
const QVariantMap& getStandard() { return _standard; }
|
||||
|
||||
private:
|
||||
|
||||
// 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);
|
||||
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);
|
||||
|
||||
friend class MappingBuilderProxy;
|
||||
friend class RouteBuilderProxy;
|
||||
private:
|
||||
uint16_t _nextFunctionId;
|
||||
InputToEndpointMap _endpoints;
|
||||
EndpointPairMap _compositeEndpoints;
|
||||
|
||||
ValueMap _overrideValues;
|
||||
MappingMap _mappingsByName;
|
||||
MappingStack _activeMappings;
|
||||
|
||||
QVariantMap _hardware;
|
||||
QVariantMap _actions;
|
||||
QVariantMap _standard;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -12,10 +12,11 @@
|
|||
|
||||
#include "Endpoint.h"
|
||||
#include "Filter.h"
|
||||
#include "Logging.h"
|
||||
|
||||
class QJsonObject;
|
||||
|
||||
namespace Controllers {
|
||||
namespace controller {
|
||||
|
||||
/*
|
||||
* encapsulates a source, destination and filters to apply
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
class QJsonObject;
|
||||
|
||||
namespace Controllers {
|
||||
namespace controller {
|
||||
|
||||
/*
|
||||
* encapsulates a source, destination and filters to apply
|
||||
|
|
|
@ -12,22 +12,33 @@
|
|||
#include <QtCore/QDebug>
|
||||
|
||||
#include "RouteBuilderProxy.h"
|
||||
#include "../NewControllerScriptingInterface.h"
|
||||
#include "../Logging.h"
|
||||
|
||||
namespace Controllers {
|
||||
namespace controller {
|
||||
|
||||
QObject* MappingBuilderProxy::from(const QString& source) {
|
||||
qDebug() << "Creating new Route builder proxy from " << source;
|
||||
QObject* MappingBuilderProxy::from(const QJSValue& source) {
|
||||
qCDebug(controllers) << "Creating new Route builder proxy from " << source.toString();
|
||||
auto sourceEndpoint = _parent.endpointFor(source);
|
||||
return from(sourceEndpoint);
|
||||
}
|
||||
|
||||
QObject* MappingBuilderProxy::from(const QScriptValue& source) {
|
||||
qCDebug(controllers) << "Creating new Route builder proxy from " << source.toString();
|
||||
auto sourceEndpoint = _parent.endpointFor(source);
|
||||
return from(sourceEndpoint);
|
||||
}
|
||||
|
||||
QObject* MappingBuilderProxy::from(const Endpoint::Pointer& source) {
|
||||
auto route = Route::Pointer(new Route());
|
||||
route->_source = endpointFor(source);
|
||||
return new RouteBuilderProxy(this, route);
|
||||
route->_source = source;
|
||||
return new RouteBuilderProxy(_parent, _mapping, route);
|
||||
}
|
||||
|
||||
Endpoint::Pointer MappingBuilderProxy::endpointFor(const QString& endpoint) {
|
||||
static QHash<QString, Endpoint::Pointer> ENDPOINTS;
|
||||
if (!ENDPOINTS.contains(endpoint)) {
|
||||
ENDPOINTS[endpoint] = std::make_shared<Endpoint>();
|
||||
}
|
||||
return ENDPOINTS[endpoint];
|
||||
QObject* MappingBuilderProxy::join(const QJSValue& source1, const QJSValue& source2) {
|
||||
auto source1Endpoint = _parent.endpointFor(source1);
|
||||
auto source2Endpoint = _parent.endpointFor(source2);
|
||||
return from(_parent.compositeEndpointFor(source1Endpoint, source2Endpoint));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -13,20 +13,30 @@
|
|||
#include <QtCore/QString>
|
||||
|
||||
#include "../Mapping.h"
|
||||
#include "../Endpoint.h"
|
||||
|
||||
namespace Controllers {
|
||||
class QJSValue;
|
||||
class QScriptValue;
|
||||
|
||||
namespace controller {
|
||||
|
||||
class NewControllerScriptingInterface;
|
||||
|
||||
class MappingBuilderProxy : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
MappingBuilderProxy(Mapping::Pointer mapping)
|
||||
: _mapping(mapping) { }
|
||||
MappingBuilderProxy(NewControllerScriptingInterface& parent, Mapping::Pointer mapping)
|
||||
: _parent(parent), _mapping(mapping) { }
|
||||
|
||||
Q_INVOKABLE QObject* from(const QString& fromEndpoint);
|
||||
Q_INVOKABLE QObject* from(const QJSValue& source);
|
||||
Q_INVOKABLE QObject* from(const QScriptValue& source);
|
||||
|
||||
Q_INVOKABLE QObject* join(const QJSValue& source1, const QJSValue& source2);
|
||||
protected:
|
||||
QObject* from(const Endpoint::Pointer& source);
|
||||
|
||||
friend class RouteBuilderProxy;
|
||||
Endpoint::Pointer endpointFor(const QString& endpoint);
|
||||
NewControllerScriptingInterface& _parent;
|
||||
Mapping::Pointer _mapping;
|
||||
};
|
||||
|
||||
|
|
|
@ -12,17 +12,47 @@
|
|||
#include <GLMHelpers.h>
|
||||
|
||||
#include "MappingBuilderProxy.h"
|
||||
#include "../NewControllerScriptingInterface.h"
|
||||
#include "../Logging.h"
|
||||
|
||||
namespace Controllers {
|
||||
namespace controller {
|
||||
|
||||
void RouteBuilderProxy::to(const QString& destination) {
|
||||
qDebug() << "Completed route: " << destination;
|
||||
void RouteBuilderProxy::to(const QJSValue& destination) {
|
||||
qCDebug(controllers) << "Completing route " << destination.toString();
|
||||
auto destinationEndpoint = _parent.endpointFor(destination);
|
||||
return to(destinationEndpoint);
|
||||
}
|
||||
|
||||
void RouteBuilderProxy::to(const QScriptValue& destination) {
|
||||
qCDebug(controllers) << "Completing route " << destination.toString();
|
||||
auto destinationEndpoint = _parent.endpointFor(destination);
|
||||
return to(destinationEndpoint);
|
||||
}
|
||||
|
||||
void RouteBuilderProxy::to(const Endpoint::Pointer& destination) {
|
||||
auto sourceEndpoint = _route->_source;
|
||||
auto& mapping = _parent->_mapping;
|
||||
mapping->_channelMappings[sourceEndpoint].push_back(_route);
|
||||
_route->_destination = destination;
|
||||
_mapping->_channelMappings[sourceEndpoint].push_back(_route);
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
QObject* RouteBuilderProxy::filter(const QJSValue& expression) {
|
||||
if (expression.isCallable()) {
|
||||
addFilter([=](float value) {
|
||||
QJSValue originalExpression = expression;
|
||||
QJSValueList params({ QJSValue(value) });
|
||||
auto result = originalExpression.call(params);
|
||||
return (float)(result.toNumber());
|
||||
});
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
QObject* RouteBuilderProxy::filter(const QScriptValue& expression) {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
QObject* RouteBuilderProxy::clamp(float min, float max) {
|
||||
addFilter([=](float value) {
|
||||
return glm::clamp(value, min, max);
|
||||
|
@ -45,7 +75,7 @@ QObject* RouteBuilderProxy::deadZone(float min) {
|
|||
assert(min < 1.0f);
|
||||
float scale = 1.0f / (1.0f - min);
|
||||
addFilter([=](float value) {
|
||||
if (value < min) {
|
||||
if (abs(value) < min) {
|
||||
return 0.0f;
|
||||
}
|
||||
return (value - min) * scale;
|
||||
|
@ -68,6 +98,40 @@ QObject* RouteBuilderProxy::constrainToPositiveInteger() {
|
|||
return this;
|
||||
}
|
||||
|
||||
|
||||
class PulseFilter : public Filter {
|
||||
public:
|
||||
PulseFilter(float interval) : _interval(interval) {}
|
||||
|
||||
virtual float apply(float value) const override {
|
||||
float result = 0.0;
|
||||
|
||||
if (0.0 != value) {
|
||||
float now = secTimestampNow();
|
||||
float delta = now - _lastEmitTime;
|
||||
if (delta >= _interval) {
|
||||
_lastEmitTime = now;
|
||||
result = value;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
mutable float _lastEmitTime{ -std::numeric_limits<float>::max() };
|
||||
const float _interval;
|
||||
};
|
||||
|
||||
|
||||
QObject* RouteBuilderProxy::pulse(float interval) {
|
||||
Filter::Pointer filter = std::make_shared<PulseFilter>(interval);
|
||||
addFilter(filter);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void RouteBuilderProxy::addFilter(Filter::Lambda lambda) {
|
||||
Filter::Pointer filterPointer = std::make_shared < LambdaFilter > (lambda);
|
||||
addFilter(filterPointer);
|
||||
|
|
|
@ -12,19 +12,28 @@
|
|||
#include <QtCore/QObject>
|
||||
#include "../Filter.h"
|
||||
#include "../Route.h"
|
||||
#include "../Mapping.h"
|
||||
|
||||
namespace Controllers {
|
||||
class QJSValue;
|
||||
class QScriptValue;
|
||||
|
||||
class MappingBuilderProxy;
|
||||
namespace controller {
|
||||
|
||||
class NewControllerScriptingInterface;
|
||||
|
||||
class RouteBuilderProxy : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
RouteBuilderProxy(MappingBuilderProxy* parent, Route::Pointer route)
|
||||
: _parent(parent), _route(route) { }
|
||||
RouteBuilderProxy(NewControllerScriptingInterface& parent, Mapping::Pointer mapping, Route::Pointer route)
|
||||
: _parent(parent), _mapping(mapping), _route(route) { }
|
||||
|
||||
Q_INVOKABLE void to(const QString& destination);
|
||||
Q_INVOKABLE void to(const QJSValue& destination);
|
||||
Q_INVOKABLE void to(const QScriptValue& destination);
|
||||
|
||||
Q_INVOKABLE QObject* filter(const QJSValue& expression);
|
||||
Q_INVOKABLE QObject* filter(const QScriptValue& expression);
|
||||
Q_INVOKABLE QObject* clamp(float min, float max);
|
||||
Q_INVOKABLE QObject* pulse(float interval);
|
||||
Q_INVOKABLE QObject* scale(float multiplier);
|
||||
Q_INVOKABLE QObject* invert();
|
||||
Q_INVOKABLE QObject* deadZone(float min);
|
||||
|
@ -32,10 +41,13 @@ class RouteBuilderProxy : public QObject {
|
|||
Q_INVOKABLE QObject* constrainToPositiveInteger();
|
||||
|
||||
private:
|
||||
void to(const Endpoint::Pointer& destination);
|
||||
void addFilter(Filter::Lambda lambda);
|
||||
void addFilter(Filter::Pointer filter);
|
||||
Mapping::Pointer _mapping;
|
||||
Route::Pointer _route;
|
||||
MappingBuilderProxy* _parent;
|
||||
|
||||
NewControllerScriptingInterface& _parent;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -22,8 +22,8 @@ InputPluginList getInputPlugins() {
|
|||
InputPlugin* PLUGIN_POOL[] = {
|
||||
new KeyboardMouseDevice(),
|
||||
new SDL2Manager(),
|
||||
new SixenseManager(),
|
||||
new ViveControllerManager(),
|
||||
//new SixenseManager(),
|
||||
//new ViveControllerManager(),
|
||||
nullptr
|
||||
};
|
||||
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
|
||||
#include "Joystick.h"
|
||||
|
||||
#include "StandardControls.h"
|
||||
|
||||
const float CONTROLLER_THRESHOLD = 0.3f;
|
||||
|
||||
#ifdef HAVE_SDL2
|
||||
|
@ -55,39 +57,14 @@ void Joystick::focusOutEvent() {
|
|||
};
|
||||
|
||||
#ifdef HAVE_SDL2
|
||||
|
||||
void Joystick::handleAxisEvent(const SDL_ControllerAxisEvent& event) {
|
||||
SDL_GameControllerAxis axis = (SDL_GameControllerAxis) event.axis;
|
||||
|
||||
switch (axis) {
|
||||
case SDL_CONTROLLER_AXIS_LEFTX:
|
||||
_axisStateMap[makeInput(LEFT_AXIS_X_POS).getChannel()] = (event.value > 0.0f) ? event.value / MAX_AXIS : 0.0f;
|
||||
_axisStateMap[makeInput(LEFT_AXIS_X_NEG).getChannel()] = (event.value < 0.0f) ? -event.value / MAX_AXIS : 0.0f;
|
||||
break;
|
||||
case SDL_CONTROLLER_AXIS_LEFTY:
|
||||
_axisStateMap[makeInput(LEFT_AXIS_Y_POS).getChannel()] = (event.value > 0.0f) ? event.value / MAX_AXIS : 0.0f;
|
||||
_axisStateMap[makeInput(LEFT_AXIS_Y_NEG).getChannel()] = (event.value < 0.0f) ? -event.value / MAX_AXIS : 0.0f;
|
||||
break;
|
||||
case SDL_CONTROLLER_AXIS_RIGHTX:
|
||||
_axisStateMap[makeInput(RIGHT_AXIS_X_POS).getChannel()] = (event.value > 0.0f) ? event.value / MAX_AXIS : 0.0f;
|
||||
_axisStateMap[makeInput(RIGHT_AXIS_X_NEG).getChannel()] = (event.value < 0.0f) ? -event.value / MAX_AXIS : 0.0f;
|
||||
break;
|
||||
case SDL_CONTROLLER_AXIS_RIGHTY:
|
||||
_axisStateMap[makeInput(RIGHT_AXIS_Y_POS).getChannel()] = (event.value > 0.0f) ? event.value / MAX_AXIS : 0.0f;
|
||||
_axisStateMap[makeInput(RIGHT_AXIS_Y_NEG).getChannel()] = (event.value < 0.0f) ? -event.value / MAX_AXIS : 0.0f;
|
||||
break;
|
||||
case SDL_CONTROLLER_AXIS_TRIGGERRIGHT:
|
||||
_axisStateMap[makeInput(RIGHT_SHOULDER).getChannel()] = event.value / MAX_AXIS;
|
||||
break;
|
||||
case SDL_CONTROLLER_AXIS_TRIGGERLEFT:
|
||||
_axisStateMap[makeInput(LEFT_SHOULDER).getChannel()] = event.value / MAX_AXIS;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
_axisStateMap[makeInput((Controllers::StandardAxisChannel)axis).getChannel()] = (float)event.value / MAX_AXIS;
|
||||
}
|
||||
|
||||
void Joystick::handleButtonEvent(const SDL_ControllerButtonEvent& event) {
|
||||
auto input = makeInput((SDL_GameControllerButton) event.button);
|
||||
auto input = makeInput((Controllers::StandardButtonChannel)event.button);
|
||||
bool newValue = event.state == SDL_PRESSED;
|
||||
if (newValue) {
|
||||
_buttonPressedMap.insert(input.getChannel());
|
||||
|
@ -95,6 +72,7 @@ void Joystick::handleButtonEvent(const SDL_ControllerButtonEvent& event) {
|
|||
_buttonPressedMap.erase(input.getChannel());
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -107,32 +85,57 @@ void Joystick::registerToUserInputMapper(UserInputMapper& mapper) {
|
|||
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;
|
||||
#ifdef HAVE_SDL2
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_A), "Bottom Button"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_B), "Right Button"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_X), "Left Button"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_Y), "Top Button"));
|
||||
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_DPAD_UP), "DPad Up"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_DPAD_DOWN), "DPad Down"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_DPAD_LEFT), "DPad Left"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_DPAD_RIGHT), "DPad Right"));
|
||||
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_LEFTSHOULDER), "L1"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), "R1"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_SHOULDER), "L2"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_SHOULDER), "R2"));
|
||||
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_Y_NEG), "Left Stick Up"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_Y_POS), "Left Stick Down"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_X_POS), "Left Stick Right"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_X_NEG), "Left Stick Left"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_Y_NEG), "Right Stick Up"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_Y_POS), "Right Stick Down"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_X_POS), "Right Stick Right"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_X_NEG), "Right Stick Left"));
|
||||
// Buttons
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::A), "A"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::B), "B"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::X), "X"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::Y), "Y"));
|
||||
|
||||
// DPad
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DU), "DU"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DD), "DD"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DL), "DL"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DR), "DR"));
|
||||
|
||||
// Bumpers
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LB), "LB"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RB), "RB"));
|
||||
|
||||
// Stick press
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LS), "LS"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RS), "RS"));
|
||||
|
||||
// Center buttons
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::START), "Start"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::BACK), "Back"));
|
||||
|
||||
// Analog sticks
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LY), "LY"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LX), "LX"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RY), "RY"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RX), "RX"));
|
||||
|
||||
// Triggers
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LT), "LT"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RT), "RT"));
|
||||
|
||||
// Aliases, PlayStation style names
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LB), "L1"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RB), "R1"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LT), "L2"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RT), "R2"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LS), "L3"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RS), "R3"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::BACK), "Select"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::A), "Cross"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::B), "Circle"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::X), "Square"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::Y), "Triangle"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DU), "Up"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DD), "Down"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DL), "Left"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DR), "Right"));
|
||||
|
||||
#endif
|
||||
return availableInputs;
|
||||
};
|
||||
proxy->resetDeviceBindings = [this, &mapper] () -> bool {
|
||||
|
@ -150,76 +153,15 @@ void Joystick::assignDefaultInputMapping(UserInputMapper& mapper) {
|
|||
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::LONGITUDINAL_FORWARD, makeInput(LEFT_AXIS_Y_NEG), JOYSTICK_MOVE_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(LEFT_AXIS_Y_POS), JOYSTICK_MOVE_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(LEFT_AXIS_X_POS), JOYSTICK_MOVE_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(LEFT_AXIS_X_NEG), JOYSTICK_MOVE_SPEED);
|
||||
|
||||
// Right Joystick: Camera orientation
|
||||
mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(RIGHT_AXIS_X_POS), JOYSTICK_YAW_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(RIGHT_AXIS_X_NEG), JOYSTICK_YAW_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(RIGHT_AXIS_Y_NEG), JOYSTICK_PITCH_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(RIGHT_AXIS_Y_POS), JOYSTICK_PITCH_SPEED);
|
||||
|
||||
// Dpad movement
|
||||
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(SDL_CONTROLLER_BUTTON_DPAD_UP), DPAD_MOVE_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(SDL_CONTROLLER_BUTTON_DPAD_DOWN), DPAD_MOVE_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(SDL_CONTROLLER_BUTTON_DPAD_RIGHT), DPAD_MOVE_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(SDL_CONTROLLER_BUTTON_DPAD_LEFT), DPAD_MOVE_SPEED);
|
||||
|
||||
// Button controls
|
||||
mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(SDL_CONTROLLER_BUTTON_Y), DPAD_MOVE_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(SDL_CONTROLLER_BUTTON_X), DPAD_MOVE_SPEED);
|
||||
|
||||
// Zoom
|
||||
mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(RIGHT_SHOULDER), BOOM_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(LEFT_SHOULDER), BOOM_SPEED);
|
||||
|
||||
|
||||
// Hold front right shoulder button for precision controls
|
||||
// Left Joystick: Movement, strafing
|
||||
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(LEFT_AXIS_Y_NEG), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f);
|
||||
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(LEFT_AXIS_Y_POS), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(LEFT_AXIS_X_POS), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(LEFT_AXIS_X_NEG), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f);
|
||||
|
||||
// Right Joystick: Camera orientation
|
||||
mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(RIGHT_AXIS_X_POS), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_YAW_SPEED/2.0f);
|
||||
mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(RIGHT_AXIS_X_NEG), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_YAW_SPEED/2.0f);
|
||||
mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(RIGHT_AXIS_Y_NEG), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_PITCH_SPEED/2.0f);
|
||||
mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(RIGHT_AXIS_Y_POS), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_PITCH_SPEED/2.0f);
|
||||
|
||||
// Dpad movement
|
||||
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(SDL_CONTROLLER_BUTTON_DPAD_UP), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f);
|
||||
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(SDL_CONTROLLER_BUTTON_DPAD_DOWN), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(SDL_CONTROLLER_BUTTON_DPAD_RIGHT), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(SDL_CONTROLLER_BUTTON_DPAD_LEFT), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f);
|
||||
|
||||
// Button controls
|
||||
mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(SDL_CONTROLLER_BUTTON_Y), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f);
|
||||
mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(SDL_CONTROLLER_BUTTON_X), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f);
|
||||
|
||||
// Zoom
|
||||
mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(RIGHT_SHOULDER), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), BOOM_SPEED/2.0f);
|
||||
mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(LEFT_SHOULDER), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), BOOM_SPEED/2.0f);
|
||||
|
||||
mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(SDL_CONTROLLER_BUTTON_LEFTSHOULDER));
|
||||
|
||||
mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(SDL_CONTROLLER_BUTTON_B));
|
||||
mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(SDL_CONTROLLER_BUTTON_A));
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef HAVE_SDL2
|
||||
UserInputMapper::Input Joystick::makeInput(SDL_GameControllerButton button) {
|
||||
UserInputMapper::Input Joystick::makeInput(Controllers::StandardButtonChannel button) {
|
||||
return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON);
|
||||
}
|
||||
#endif
|
||||
|
||||
UserInputMapper::Input Joystick::makeInput(Joystick::JoystickAxisChannel axis) {
|
||||
UserInputMapper::Input Joystick::makeInput(Controllers::StandardAxisChannel axis) {
|
||||
return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS);
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#endif
|
||||
|
||||
#include "InputDevice.h"
|
||||
#include "StandardControls.h"
|
||||
|
||||
class Joystick : public QObject, public InputDevice {
|
||||
Q_OBJECT
|
||||
|
@ -31,18 +32,6 @@ class Joystick : public QObject, public InputDevice {
|
|||
#endif
|
||||
|
||||
public:
|
||||
enum JoystickAxisChannel {
|
||||
LEFT_AXIS_X_POS = 0,
|
||||
LEFT_AXIS_X_NEG,
|
||||
LEFT_AXIS_Y_POS,
|
||||
LEFT_AXIS_Y_NEG,
|
||||
RIGHT_AXIS_X_POS,
|
||||
RIGHT_AXIS_X_NEG,
|
||||
RIGHT_AXIS_Y_POS,
|
||||
RIGHT_AXIS_Y_NEG,
|
||||
RIGHT_SHOULDER,
|
||||
LEFT_SHOULDER,
|
||||
};
|
||||
|
||||
const QString& getName() const { return _name; }
|
||||
|
||||
|
@ -55,10 +44,8 @@ public:
|
|||
Joystick() : InputDevice("Joystick") {}
|
||||
~Joystick();
|
||||
|
||||
#ifdef HAVE_SDL2
|
||||
UserInputMapper::Input makeInput(SDL_GameControllerButton button);
|
||||
#endif
|
||||
UserInputMapper::Input makeInput(Joystick::JoystickAxisChannel axis);
|
||||
UserInputMapper::Input makeInput(Controllers::StandardButtonChannel button);
|
||||
UserInputMapper::Input makeInput(Controllers::StandardAxisChannel axis);
|
||||
|
||||
#ifdef HAVE_SDL2
|
||||
Joystick(SDL_JoystickID instanceId, const QString& name, SDL_GameController* sdlGameController);
|
||||
|
|
|
@ -10,6 +10,11 @@
|
|||
//
|
||||
#include "KeyboardMouseDevice.h"
|
||||
|
||||
#include <QtGUI/QKeyEvent>
|
||||
#include <QtGUI/QMouseEvent>
|
||||
#include <QtGUI/QTouchEvent>
|
||||
|
||||
|
||||
const QString KeyboardMouseDevice::NAME = "Keyboard/Mouse";
|
||||
|
||||
void KeyboardMouseDevice::update(float deltaTime, bool jointsCaptured) {
|
||||
|
@ -81,7 +86,7 @@ void KeyboardMouseDevice::wheelEvent(QWheelEvent* event) {
|
|||
_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 evalAverageTouchPoints(const QList<QTouchEvent::TouchPoint>& points) {
|
||||
glm::vec2 averagePoint(0.0f);
|
||||
if (points.count() > 0) {
|
||||
for (auto& point : points) {
|
||||
|
|
|
@ -12,11 +12,15 @@
|
|||
#ifndef hifi_KeyboardMouseDevice_h
|
||||
#define hifi_KeyboardMouseDevice_h
|
||||
|
||||
#include <QTouchEvent>
|
||||
#include <chrono>
|
||||
#include "InputDevice.h"
|
||||
#include "InputPlugin.h"
|
||||
|
||||
class QTouchEvent;
|
||||
class QKeyEvent;
|
||||
class QMouseEvent;
|
||||
class QWheelEvent;
|
||||
|
||||
class KeyboardMouseDevice : public InputPlugin, public InputDevice {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
@ -100,7 +104,6 @@ protected:
|
|||
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;
|
||||
};
|
||||
|
|
|
@ -23,11 +23,6 @@ StandardController::~StandardController() {
|
|||
}
|
||||
|
||||
void StandardController::update(float deltaTime, bool jointsCaptured) {
|
||||
for (auto axisState : _axisStateMap) {
|
||||
if (fabsf(axisState.second) < CONTROLLER_THRESHOLD) {
|
||||
_axisStateMap[axisState.first] = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StandardController::focusOutEvent() {
|
||||
|
@ -44,119 +39,85 @@ void StandardController::registerToUserInputMapper(UserInputMapper& mapper) {
|
|||
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(STANDARD_CONTROLLER_BUTTON_A), "Bottom Button"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_B), "Right Button"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_X), "Left Button"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_Y), "Top Button"));
|
||||
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_UP), "DPad Up"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_DOWN), "DPad Down"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_LEFT), "DPad Left"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_RIGHT), "DPad Right"));
|
||||
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_LEFTSHOULDER), "L1"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), "R1"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_SHOULDER), "L2"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_SHOULDER), "R2"));
|
||||
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_Y_NEG), "Left Stick Up"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_Y_POS), "Left Stick Down"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_X_POS), "Left Stick Right"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_X_NEG), "Left Stick Left"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_Y_NEG), "Right Stick Up"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_Y_POS), "Right Stick Down"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_X_POS), "Right Stick Right"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_X_NEG), "Right Stick Left"));
|
||||
// Buttons
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::A), "A"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::B), "B"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::X), "X"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::Y), "Y"));
|
||||
|
||||
// DPad
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DU), "DU"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DD), "DD"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DL), "DL"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DR), "DR"));
|
||||
|
||||
// Bumpers
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LB), "LB"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RB), "RB"));
|
||||
|
||||
// Stick press
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LS), "LS"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RS), "RS"));
|
||||
|
||||
// Center buttons
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::START), "Start"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::BACK), "Back"));
|
||||
|
||||
// Analog sticks
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LY), "LY"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LX), "LX"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RY), "RY"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RX), "RX"));
|
||||
|
||||
// Triggers
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LT), "LT"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RT), "RT"));
|
||||
|
||||
// Poses
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LeftPose), "LeftPose"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RightPose), "RightPose"));
|
||||
|
||||
// Aliases, PlayStation style names
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LB), "L1"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RB), "R1"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LT), "L2"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RT), "R2"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LS), "L3"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RS), "R3"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::BACK), "Select"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::A), "Cross"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::B), "Circle"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::X), "Square"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::Y), "Triangle"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DU), "Up"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DD), "Down"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DL), "Left"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DR), "Right"));
|
||||
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_HAND), "Left Hand"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_HAND), "Right Hand"));
|
||||
|
||||
return availableInputs;
|
||||
};
|
||||
|
||||
proxy->resetDeviceBindings = [this, &mapper] () -> bool {
|
||||
mapper.removeAllInputChannelsForDevice(_deviceID);
|
||||
this->assignDefaultInputMapping(mapper);
|
||||
return true;
|
||||
};
|
||||
|
||||
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 StandardController: Movement, strafing
|
||||
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(LEFT_AXIS_Y_NEG), JOYSTICK_MOVE_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(LEFT_AXIS_Y_POS), JOYSTICK_MOVE_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(LEFT_AXIS_X_POS), JOYSTICK_MOVE_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(LEFT_AXIS_X_NEG), JOYSTICK_MOVE_SPEED);
|
||||
|
||||
// Right StandardController: Camera orientation
|
||||
mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(RIGHT_AXIS_X_POS), JOYSTICK_YAW_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(RIGHT_AXIS_X_NEG), JOYSTICK_YAW_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(RIGHT_AXIS_Y_NEG), JOYSTICK_PITCH_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(RIGHT_AXIS_Y_POS), JOYSTICK_PITCH_SPEED);
|
||||
|
||||
// Dpad movement
|
||||
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_UP), DPAD_MOVE_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_DOWN), DPAD_MOVE_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_RIGHT), DPAD_MOVE_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_LEFT), DPAD_MOVE_SPEED);
|
||||
|
||||
// Button controls
|
||||
mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(STANDARD_CONTROLLER_BUTTON_Y), DPAD_MOVE_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(STANDARD_CONTROLLER_BUTTON_X), DPAD_MOVE_SPEED);
|
||||
|
||||
// Zoom
|
||||
mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(RIGHT_SHOULDER), BOOM_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(LEFT_SHOULDER), BOOM_SPEED);
|
||||
|
||||
|
||||
// Hold front right shoulder button for precision controls
|
||||
// Left StandardController: Movement, strafing
|
||||
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(LEFT_AXIS_Y_NEG), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f);
|
||||
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(LEFT_AXIS_Y_POS), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(LEFT_AXIS_X_POS), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(LEFT_AXIS_X_NEG), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f);
|
||||
|
||||
// Right StandardController: Camera orientation
|
||||
mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(RIGHT_AXIS_X_POS), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_YAW_SPEED/2.0f);
|
||||
mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(RIGHT_AXIS_X_NEG), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_YAW_SPEED/2.0f);
|
||||
mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(RIGHT_AXIS_Y_NEG), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_PITCH_SPEED/2.0f);
|
||||
mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(RIGHT_AXIS_Y_POS), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_PITCH_SPEED/2.0f);
|
||||
|
||||
// Dpad movement
|
||||
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_UP), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f);
|
||||
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_DOWN), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_RIGHT), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_LEFT), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f);
|
||||
|
||||
// Button controls
|
||||
mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(STANDARD_CONTROLLER_BUTTON_Y), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f);
|
||||
mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(STANDARD_CONTROLLER_BUTTON_X), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f);
|
||||
|
||||
// Zoom
|
||||
mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(RIGHT_SHOULDER), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), BOOM_SPEED/2.0f);
|
||||
mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(LEFT_SHOULDER), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), BOOM_SPEED/2.0f);
|
||||
|
||||
mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(STANDARD_CONTROLLER_BUTTON_LEFTSHOULDER));
|
||||
|
||||
mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(STANDARD_CONTROLLER_BUTTON_B));
|
||||
mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(STANDARD_CONTROLLER_BUTTON_A));
|
||||
}
|
||||
|
||||
UserInputMapper::Input StandardController::makeInput(StandardController::StandardControllerButtonChannel button) {
|
||||
UserInputMapper::Input StandardController::makeInput(Controllers::StandardButtonChannel button) {
|
||||
return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON);
|
||||
}
|
||||
|
||||
UserInputMapper::Input StandardController::makeInput(StandardController::StandardControllerAxisChannel axis) {
|
||||
UserInputMapper::Input StandardController::makeInput(Controllers::StandardAxisChannel axis) {
|
||||
return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS);
|
||||
}
|
||||
|
||||
UserInputMapper::Input StandardController::makeInput(StandardController::StandardControllerPoseChannel pose) {
|
||||
UserInputMapper::Input StandardController::makeInput(Controllers::StandardPoseChannel pose) {
|
||||
return UserInputMapper::Input(_deviceID, pose, UserInputMapper::ChannelType::POSE);
|
||||
}
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
|
||||
#include "InputDevice.h"
|
||||
|
||||
#include "StandardControls.h"
|
||||
|
||||
typedef std::shared_ptr<StandardController> StandardControllerPointer;
|
||||
|
||||
class StandardController : public QObject, public InputDevice {
|
||||
|
@ -24,37 +26,6 @@ class StandardController : public QObject, public InputDevice {
|
|||
Q_PROPERTY(QString name READ getName)
|
||||
|
||||
public:
|
||||
enum StandardControllerAxisChannel {
|
||||
LEFT_AXIS_X_POS = 0,
|
||||
LEFT_AXIS_X_NEG,
|
||||
LEFT_AXIS_Y_POS,
|
||||
LEFT_AXIS_Y_NEG,
|
||||
RIGHT_AXIS_X_POS,
|
||||
RIGHT_AXIS_X_NEG,
|
||||
RIGHT_AXIS_Y_POS,
|
||||
RIGHT_AXIS_Y_NEG,
|
||||
RIGHT_SHOULDER,
|
||||
LEFT_SHOULDER,
|
||||
};
|
||||
enum StandardControllerButtonChannel {
|
||||
STANDARD_CONTROLLER_BUTTON_A = 0,
|
||||
STANDARD_CONTROLLER_BUTTON_B,
|
||||
STANDARD_CONTROLLER_BUTTON_X,
|
||||
STANDARD_CONTROLLER_BUTTON_Y,
|
||||
|
||||
STANDARD_CONTROLLER_BUTTON_DPAD_UP,
|
||||
STANDARD_CONTROLLER_BUTTON_DPAD_DOWN,
|
||||
STANDARD_CONTROLLER_BUTTON_DPAD_LEFT,
|
||||
STANDARD_CONTROLLER_BUTTON_DPAD_RIGHT,
|
||||
|
||||
STANDARD_CONTROLLER_BUTTON_LEFTSHOULDER,
|
||||
STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER,
|
||||
};
|
||||
|
||||
enum StandardControllerPoseChannel {
|
||||
LEFT_HAND = 0,
|
||||
RIGHT_HAND,
|
||||
};
|
||||
|
||||
const QString& getName() const { return _name; }
|
||||
|
||||
|
@ -67,9 +38,9 @@ public:
|
|||
StandardController() : InputDevice("Standard") {}
|
||||
~StandardController();
|
||||
|
||||
UserInputMapper::Input makeInput(StandardController::StandardControllerButtonChannel button);
|
||||
UserInputMapper::Input makeInput(StandardController::StandardControllerAxisChannel axis);
|
||||
UserInputMapper::Input makeInput(StandardController::StandardControllerPoseChannel pose);
|
||||
UserInputMapper::Input makeInput(Controllers::StandardButtonChannel button);
|
||||
UserInputMapper::Input makeInput(Controllers::StandardAxisChannel axis);
|
||||
UserInputMapper::Input makeInput(Controllers::StandardPoseChannel pose);
|
||||
|
||||
private:
|
||||
};
|
||||
|
|
55
libraries/input-plugins/src/input-plugins/StandardControls.h
Normal file
55
libraries/input-plugins/src/input-plugins/StandardControls.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis 2015/10/09
|
||||
// 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
|
||||
|
||||
namespace Controllers {
|
||||
|
||||
// Needs to match order and values of SDL_GameControllerButton
|
||||
enum StandardButtonChannel {
|
||||
// Button quad
|
||||
A = 0,
|
||||
B,
|
||||
X,
|
||||
Y,
|
||||
// Center buttons
|
||||
BACK,
|
||||
GUIDE,
|
||||
START,
|
||||
// Stick press
|
||||
LS,
|
||||
RS,
|
||||
// Bumper press
|
||||
LB,
|
||||
RB,
|
||||
// DPad
|
||||
DU,
|
||||
DD,
|
||||
DL,
|
||||
DR
|
||||
};
|
||||
|
||||
// Needs to match order and values of SDL_GameControllerAxis
|
||||
enum StandardAxisChannel {
|
||||
// Left Analog stick
|
||||
LX = 0,
|
||||
LY,
|
||||
// Right Analog stick
|
||||
RX,
|
||||
RY,
|
||||
// Triggers
|
||||
LT,
|
||||
RT
|
||||
};
|
||||
|
||||
// No correlation to SDL
|
||||
enum StandardPoseChannel {
|
||||
LeftPose = 0,
|
||||
RightPose
|
||||
};
|
||||
|
||||
}
|
|
@ -1,335 +1,368 @@
|
|||
//
|
||||
// UserInputMapper.cpp
|
||||
// input-plugins/src/input-plugins
|
||||
//
|
||||
// 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 "StandardController.h"
|
||||
|
||||
// Default contruct allocate the poutput size with the current hardcoded action channels
|
||||
UserInputMapper::UserInputMapper() {
|
||||
registerStandardDevice();
|
||||
assignDefaulActionScales();
|
||||
createActionNames();
|
||||
}
|
||||
|
||||
UserInputMapper::~UserInputMapper() {
|
||||
}
|
||||
|
||||
|
||||
bool UserInputMapper::registerDevice(uint16 deviceID, const DeviceProxy::Pointer& proxy){
|
||||
proxy->_name += " (" + QString::number(deviceID) + ")";
|
||||
_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();
|
||||
}
|
||||
}
|
||||
|
||||
QString UserInputMapper::getDeviceName(uint16 deviceID) {
|
||||
if (_registeredDevices.find(deviceID) != _registeredDevices.end()) {
|
||||
return _registeredDevices[deviceID]->_name;
|
||||
}
|
||||
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) {
|
||||
for (auto device : _registeredDevices) {
|
||||
if (device.second->_name.split(" (")[0] == name) {
|
||||
return device.first;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
QVector<QString> UserInputMapper::getDeviceNames() {
|
||||
QVector<QString> result;
|
||||
for (auto device : _registeredDevices) {
|
||||
QString deviceName = device.second->_name.split(" (")[0];
|
||||
result << deviceName;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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 UserInputMapper::update(float deltaTime) {
|
||||
|
||||
// Reset the axis state for next loop
|
||||
for (auto& channel : _actionStates) {
|
||||
channel = 0.0f;
|
||||
}
|
||||
|
||||
for (auto& channel : _poseStates) {
|
||||
channel = PoseValue();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// Scale all the channel step with the scale
|
||||
static const float EPSILON = 0.01f;
|
||||
for (auto i = 0; i < NUM_ACTIONS; i++) {
|
||||
_actionStates[i] *= _actionScales[i];
|
||||
// Emit only on change, and emit when moving back to 0
|
||||
if (fabsf(_actionStates[i] - _lastActionStates[i]) > EPSILON) {
|
||||
_lastActionStates[i] = _actionStates[i];
|
||||
emit actionEvent(i, _actionStates[i]);
|
||||
}
|
||||
// TODO: emit signal for pose changes
|
||||
}
|
||||
}
|
||||
|
||||
QVector<UserInputMapper::Action> UserInputMapper::getAllActions() const {
|
||||
QVector<Action> actions;
|
||||
for (auto i = 0; i < NUM_ACTIONS; i++) {
|
||||
actions.append(Action(i));
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
// If the action isn't found, return -1
|
||||
return -1;
|
||||
}
|
||||
|
||||
QVector<QString> UserInputMapper::getActionNames() const {
|
||||
QVector<QString> result;
|
||||
for (auto i = 0; i < NUM_ACTIONS; i++) {
|
||||
result << _actionNames[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
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] = 0.5f; // .5m per unit
|
||||
_actionScales[BOOM_OUT] = 0.5f; // .5m per unit
|
||||
_actionScales[LEFT_HAND] = 1.0f; // default
|
||||
_actionScales[RIGHT_HAND] = 1.0f; // default
|
||||
_actionScales[LEFT_HAND_CLICK] = 1.0f; // on
|
||||
_actionScales[RIGHT_HAND_CLICK] = 1.0f; // on
|
||||
_actionStates[SHIFT] = 1.0f; // on
|
||||
_actionStates[ACTION1] = 1.0f; // default
|
||||
_actionStates[ACTION2] = 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";
|
||||
}
|
||||
|
||||
void UserInputMapper::registerStandardDevice() {
|
||||
_standardController = std::make_shared<StandardController>();
|
||||
_standardController->registerToUserInputMapper(*this);
|
||||
}
|
||||
//
|
||||
// UserInputMapper.cpp
|
||||
// input-plugins/src/input-plugins
|
||||
//
|
||||
// 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 "StandardController.h"
|
||||
|
||||
const UserInputMapper::Input UserInputMapper::Input::INVALID_INPUT = UserInputMapper::Input(UINT16_MAX);
|
||||
const uint16_t UserInputMapper::Input::INVALID_DEVICE = INVALID_INPUT.getDevice();
|
||||
const uint16_t UserInputMapper::Input::INVALID_CHANNEL = INVALID_INPUT.getChannel();
|
||||
const uint16_t UserInputMapper::Input::INVALID_TYPE = (uint16_t)INVALID_INPUT.getType();
|
||||
|
||||
// Default contruct allocate the poutput size with the current hardcoded action channels
|
||||
UserInputMapper::UserInputMapper() {
|
||||
registerStandardDevice();
|
||||
assignDefaulActionScales();
|
||||
createActionNames();
|
||||
}
|
||||
|
||||
UserInputMapper::~UserInputMapper() {
|
||||
}
|
||||
|
||||
|
||||
bool UserInputMapper::registerDevice(uint16 deviceID, const DeviceProxy::Pointer& proxy){
|
||||
proxy->_name += " (" + QString::number(deviceID) + ")";
|
||||
_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();
|
||||
}
|
||||
}
|
||||
|
||||
QString UserInputMapper::getDeviceName(uint16 deviceID) {
|
||||
if (_registeredDevices.find(deviceID) != _registeredDevices.end()) {
|
||||
return _registeredDevices[deviceID]->_name;
|
||||
}
|
||||
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) {
|
||||
for (auto device : _registeredDevices) {
|
||||
if (device.second->_name.split(" (")[0] == name) {
|
||||
return device.first;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
QVector<QString> UserInputMapper::getDeviceNames() {
|
||||
QVector<QString> result;
|
||||
for (auto device : _registeredDevices) {
|
||||
QString deviceName = device.second->_name.split(" (")[0];
|
||||
result << deviceName;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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 UserInputMapper::update(float deltaTime) {
|
||||
|
||||
// Reset the axis state for next loop
|
||||
for (auto& channel : _actionStates) {
|
||||
channel = 0.0f;
|
||||
}
|
||||
|
||||
for (auto& channel : _poseStates) {
|
||||
channel = PoseValue();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// Scale all the channel step with the scale
|
||||
static const float EPSILON = 0.01f;
|
||||
for (auto i = 0; i < NUM_ACTIONS; i++) {
|
||||
_actionStates[i] *= _actionScales[i];
|
||||
// Emit only on change, and emit when moving back to 0
|
||||
if (fabsf(_actionStates[i] - _lastActionStates[i]) > EPSILON) {
|
||||
_lastActionStates[i] = _actionStates[i];
|
||||
emit actionEvent(i, _actionStates[i]);
|
||||
}
|
||||
// TODO: emit signal for pose changes
|
||||
}
|
||||
}
|
||||
|
||||
QVector<UserInputMapper::Action> UserInputMapper::getAllActions() const {
|
||||
QVector<Action> actions;
|
||||
for (auto i = 0; i < NUM_ACTIONS; i++) {
|
||||
actions.append(Action(i));
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
// If the action isn't found, return -1
|
||||
return -1;
|
||||
}
|
||||
|
||||
QVector<QString> UserInputMapper::getActionNames() const {
|
||||
QVector<QString> result;
|
||||
for (auto i = 0; i < NUM_ACTIONS; i++) {
|
||||
result << _actionNames[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
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] = 0.5f; // .5m per unit
|
||||
_actionScales[BOOM_OUT] = 0.5f; // .5m per unit
|
||||
_actionScales[LEFT_HAND] = 1.0f; // default
|
||||
_actionScales[RIGHT_HAND] = 1.0f; // default
|
||||
_actionScales[LEFT_HAND_CLICK] = 1.0f; // on
|
||||
_actionScales[RIGHT_HAND_CLICK] = 1.0f; // on
|
||||
_actionStates[SHIFT] = 1.0f; // on
|
||||
_actionStates[ACTION1] = 1.0f; // default
|
||||
_actionStates[ACTION2] = 1.0f; // default
|
||||
_actionStates[TranslateX] = 1.0f; // default
|
||||
_actionStates[TranslateY] = 1.0f; // default
|
||||
_actionStates[TranslateZ] = 1.0f; // default
|
||||
_actionStates[Roll] = 1.0f; // default
|
||||
_actionStates[Pitch] = 1.0f; // default
|
||||
_actionStates[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[TranslateX] = "TranslateX";
|
||||
_actionNames[TranslateY] = "TranslateY";
|
||||
_actionNames[TranslateZ] = "TranslateZ";
|
||||
_actionNames[Roll] = "Roll";
|
||||
_actionNames[Pitch] = "Pitch";
|
||||
_actionNames[Yaw] = "Yaw";
|
||||
}
|
||||
|
||||
void UserInputMapper::registerStandardDevice() {
|
||||
_standardController = std::make_shared<StandardController>();
|
||||
_standardController->registerToUserInputMapper(*this);
|
||||
}
|
||||
|
||||
float UserInputMapper::DeviceProxy::getValue(const Input& input, int timestamp) const {
|
||||
switch (input.getType()) {
|
||||
case UserInputMapper::ChannelType::BUTTON:
|
||||
return getButton(input, timestamp) ? 1.0f : 0.0f;
|
||||
|
||||
case UserInputMapper::ChannelType::AXIS:
|
||||
return getAxis(input, timestamp);
|
||||
|
||||
case UserInputMapper::ChannelType::POSE:
|
||||
return getPose(input, timestamp)._valid ? 1.0f : 0.0f;
|
||||
|
||||
default:
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,8 +48,9 @@ public:
|
|||
union {
|
||||
struct {
|
||||
uint16 _device; // Up to 64K possible devices
|
||||
uint16 _channel : 14; // 2^14 possible channel per Device
|
||||
uint16 _channel : 13; // 2^13 possible channel per Device
|
||||
uint16 _type : 2; // 2 bits to store the Type directly in the ID
|
||||
uint16 _padding : 1; // 2 bits to store the Type directly in the ID
|
||||
};
|
||||
uint32 _id = 0; // by default Input is 0 meaning invalid
|
||||
};
|
||||
|
@ -74,13 +75,19 @@ public:
|
|||
// 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)) {}
|
||||
explicit Input(uint16 device, uint16 channel, ChannelType type) : _device(device), _channel(channel), _type(uint16(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; }
|
||||
|
||||
static const Input INVALID_INPUT;
|
||||
static const uint16 INVALID_DEVICE;
|
||||
static const uint16 INVALID_CHANNEL;
|
||||
static const uint16 INVALID_TYPE;
|
||||
};
|
||||
|
||||
|
||||
// Modifiers are just button inputID
|
||||
typedef std::vector< Input > Modifiers;
|
||||
|
||||
|
@ -121,7 +128,7 @@ public:
|
|||
PoseGetter getPose = [] (const Input& input, int timestamp) -> PoseValue { return PoseValue(); };
|
||||
AvailableInputGetter getAvailabeInputs = [] () -> AvailableInput { return QVector<InputPair>(); };
|
||||
ResetBindings resetDeviceBindings = [] () -> bool { return true; };
|
||||
|
||||
float getValue(const Input& input, int timestamp = 0) const;
|
||||
typedef std::shared_ptr<DeviceProxy> Pointer;
|
||||
};
|
||||
// GetFreeDeviceID should be called before registering a device to use an ID not used by a different device.
|
||||
|
@ -171,6 +178,13 @@ public:
|
|||
CONTEXT_MENU,
|
||||
TOGGLE_MUTE,
|
||||
|
||||
TranslateX,
|
||||
TranslateY,
|
||||
TranslateZ,
|
||||
Roll,
|
||||
Pitch,
|
||||
Yaw,
|
||||
|
||||
NUM_ACTIONS,
|
||||
};
|
||||
|
||||
|
|
99
tests/controllers/qml/Xbox.qml
Normal file
99
tests/controllers/qml/Xbox.qml
Normal file
|
@ -0,0 +1,99 @@
|
|||
import QtQuick 2.1
|
||||
import QtQuick.Controls 1.0
|
||||
import QtQuick.Layouts 1.0
|
||||
import QtQuick.Dialogs 1.0
|
||||
|
||||
import "./xbox"
|
||||
import "./controls"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property var device
|
||||
|
||||
property real scale: 1.0
|
||||
width: 300 * scale
|
||||
height: 215 * scale
|
||||
|
||||
Image {
|
||||
anchors.fill: parent
|
||||
source: "xbox/xbox360-controller-md.png"
|
||||
|
||||
LeftAnalogStick {
|
||||
device: root.device
|
||||
x: (65 * root.scale) - width / 2; y: (42 * root.scale) - height / 2
|
||||
}
|
||||
|
||||
// Left stick press
|
||||
ToggleButton {
|
||||
controlId: root.device.LS
|
||||
width: 16 * root.scale; height: 16 * root.scale
|
||||
x: (65 * root.scale) - width / 2; y: (42 * root.scale) - height / 2
|
||||
}
|
||||
|
||||
|
||||
RightAnalogStick {
|
||||
device: root.device
|
||||
x: (193 * root.scale) - width / 2; y: (96 * root.scale) - height / 2
|
||||
}
|
||||
|
||||
// Right stick press
|
||||
ToggleButton {
|
||||
controlId: root.device.RS
|
||||
width: 16 * root.scale; height: 16 * root.scale
|
||||
x: (193 * root.scale) - width / 2; y: (96 * root.scale) - height / 2
|
||||
}
|
||||
|
||||
// Left trigger
|
||||
AnalogButton {
|
||||
controlId: root.device.LT
|
||||
width: 8; height: 64
|
||||
x: (20 * root.scale); y: (7 * root.scale)
|
||||
}
|
||||
|
||||
// Right trigger
|
||||
AnalogButton {
|
||||
controlId: root.device.RT
|
||||
width: 8; height: 64
|
||||
x: (272 * root.scale); y: (7 * root.scale)
|
||||
}
|
||||
|
||||
// Left bumper
|
||||
ToggleButton {
|
||||
controlId: root.device.LB
|
||||
width: 32 * root.scale; height: 16 * root.scale
|
||||
x: (40 * root.scale); y: (7 * root.scale)
|
||||
}
|
||||
|
||||
// Right bumper
|
||||
ToggleButton {
|
||||
controlId: root.device.RB
|
||||
width: 32 * root.scale; height: 16 * root.scale
|
||||
x: (root.width - width) - (40 * root.scale); y: (7 * root.scale)
|
||||
}
|
||||
|
||||
DPad {
|
||||
device: root.device
|
||||
size: 48 * root.scale
|
||||
x: (80 * root.scale); y: (71 * root.scale)
|
||||
}
|
||||
|
||||
XboxButtons {
|
||||
device: root.device
|
||||
size: 65 * root.scale
|
||||
x: (206 * root.scale); y: (19 * root.scale)
|
||||
}
|
||||
|
||||
ToggleButton {
|
||||
controlId: root.device.Back
|
||||
width: 16 * root.scale; height: 12 * root.scale
|
||||
x: (112 * root.scale); y: (45 * root.scale)
|
||||
}
|
||||
|
||||
ToggleButton {
|
||||
controlId: root.device.Start
|
||||
width: 16 * root.scale; height: 12 * root.scale
|
||||
x: (177 * root.scale); y: (45 * root.scale)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,137 +1,97 @@
|
|||
import QtQuick 2.1
|
||||
import QtQuick.Controls 1.0
|
||||
import QtQuick.Layouts 1.0
|
||||
import QtQuick.Dialogs 1.0
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
implicitHeight: column1.height + 24
|
||||
implicitWidth: column1.width + 24
|
||||
color: "lightgray"
|
||||
|
||||
property real itemSize: 128
|
||||
|
||||
Component {
|
||||
id: graphTemplate
|
||||
Item {
|
||||
implicitHeight: canvas.height + 2 + text.height
|
||||
implicitWidth: canvas.width
|
||||
property string text: loadText
|
||||
|
||||
Canvas {
|
||||
id: canvas
|
||||
width: root.itemSize; height: root.itemSize;
|
||||
antialiasing: false
|
||||
property int controlId: control
|
||||
property real value: 0.0
|
||||
property int drawWidth: 1
|
||||
|
||||
Timer {
|
||||
interval: 50; running: true; repeat: true
|
||||
onTriggered: {
|
||||
parent.value = NewControllers.getValue(canvas.controlId)
|
||||
parent.requestPaint();
|
||||
}
|
||||
}
|
||||
|
||||
onPaint: {
|
||||
var ctx = canvas.getContext('2d');
|
||||
ctx.save();
|
||||
|
||||
var image = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
ctx.drawImage(image, -drawWidth, 0, canvas.width, canvas.height)
|
||||
ctx.fillStyle = 'green'
|
||||
// draw a filles rectangle
|
||||
var height = canvas.height * canvas.value
|
||||
ctx.fillRect(canvas.width - drawWidth, canvas.height - height,
|
||||
drawWidth, height)
|
||||
ctx.restore()
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
id: text
|
||||
text: parent.text
|
||||
anchors.topMargin: 2
|
||||
anchors.horizontalCenter: canvas.horizontalCenter
|
||||
anchors.top: canvas.bottom
|
||||
font.pointSize: 12;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
id: column1
|
||||
x: 12; y: 12
|
||||
spacing: 24
|
||||
Row {
|
||||
spacing: 16
|
||||
Loader {
|
||||
sourceComponent: graphTemplate;
|
||||
property string loadText: "Key Left"
|
||||
property int control: ControllerIds.Hardware.Keyboard2.Left
|
||||
}
|
||||
Loader {
|
||||
sourceComponent: graphTemplate;
|
||||
property string loadText: "DPad Up"
|
||||
property int control: ControllerIds.Hardware.X360Controller1.DPadUp
|
||||
}
|
||||
/*
|
||||
Loader {
|
||||
sourceComponent: graphTemplate;
|
||||
property string loadText: "Yaw Left"
|
||||
property int control: ControllerIds.Actions.YAW_LEFT
|
||||
}
|
||||
Loader {
|
||||
sourceComponent: graphTemplate;
|
||||
property string loadText: "Yaw Left"
|
||||
property int control: ControllerIds.Actions.YAW_LEFT
|
||||
}
|
||||
*/
|
||||
|
||||
// Loader { sourceComponent: graphTemplate; }
|
||||
// Loader { sourceComponent: graphTemplate; }
|
||||
// Loader { sourceComponent: graphTemplate; }
|
||||
}
|
||||
/*
|
||||
Row {
|
||||
spacing: 16
|
||||
Loader { sourceComponent: graphTemplate; }
|
||||
Loader { sourceComponent: graphTemplate; }
|
||||
Loader { sourceComponent: graphTemplate; }
|
||||
Loader { sourceComponent: graphTemplate; }
|
||||
}
|
||||
Row {
|
||||
spacing: 16
|
||||
Loader { sourceComponent: graphTemplate; }
|
||||
Loader { sourceComponent: graphTemplate; }
|
||||
Loader { sourceComponent: graphTemplate; }
|
||||
Loader { sourceComponent: graphTemplate; }
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
Button {
|
||||
text: "Go!"
|
||||
onClicked: {
|
||||
//
|
||||
|
||||
// var newMapping = NewControllers.newMapping();
|
||||
// console.log("Mapping Object " + newMapping);
|
||||
// var routeBuilder = newMapping.from("Hello");
|
||||
// console.log("Route Builder " + routeBuilder);
|
||||
// routeBuilder.clamp(0, 1).clamp(0, 1).to("Goodbye");
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
interval: 50; running: true; repeat: true
|
||||
onTriggered: {
|
||||
NewControllers.update();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
import QtQuick 2.1
|
||||
import QtQuick.Controls 1.0
|
||||
import QtQuick.Layouts 1.0
|
||||
import QtQuick.Dialogs 1.0
|
||||
|
||||
import "./xbox"
|
||||
import "./controls"
|
||||
|
||||
Column {
|
||||
id: root
|
||||
property var xbox: NewControllers.Hardware.X360Controller1
|
||||
property var actions: NewControllers.Actions
|
||||
property var standard: NewControllers.Standard
|
||||
property string mappingName: "TestMapping"
|
||||
|
||||
spacing: 12
|
||||
|
||||
Timer {
|
||||
interval: 50; running: true; repeat: true
|
||||
onTriggered: {
|
||||
NewControllers.update();
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing: 8
|
||||
Button {
|
||||
text: "Default Mapping"
|
||||
onClicked: {
|
||||
var mapping = NewControllers.newMapping("Default");
|
||||
mapping.from(xbox.A).to(standard.A);
|
||||
mapping.from(xbox.B).to(standard.B);
|
||||
mapping.from(xbox.X).to(standard.X);
|
||||
mapping.from(xbox.Y).to(standard.Y);
|
||||
mapping.from(xbox.Up).to(standard.DU);
|
||||
mapping.from(xbox.Down).to(standard.DD);
|
||||
mapping.from(xbox.Left).to(standard.DL);
|
||||
mapping.from(xbox.Right).to(standard.Right);
|
||||
mapping.from(xbox.LB).to(standard.LB);
|
||||
mapping.from(xbox.RB).to(standard.RB);
|
||||
mapping.from(xbox.LS).to(standard.LS);
|
||||
mapping.from(xbox.RS).to(standard.RS);
|
||||
mapping.from(xbox.Start).to(standard.Start);
|
||||
mapping.from(xbox.Back).to(standard.Back);
|
||||
mapping.from(xbox.LY).to(standard.LY);
|
||||
mapping.from(xbox.LX).to(standard.LX);
|
||||
mapping.from(xbox.RY).to(standard.RY);
|
||||
mapping.from(xbox.RX).to(standard.RX);
|
||||
mapping.from(xbox.LT).to(standard.LT);
|
||||
mapping.from(xbox.RT).to(standard.RT);
|
||||
NewControllers.enableMapping("Default");
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "Build Mapping"
|
||||
onClicked: {
|
||||
var mapping = NewControllers.newMapping(root.mappingName);
|
||||
// Inverting a value
|
||||
mapping.from(xbox.RY).invert().to(standard.RY);
|
||||
// Assigning a value from a function
|
||||
mapping.from(function() { return Math.sin(Date.now() / 250); }).to(standard.RX);
|
||||
// Constrainting a value to -1, 0, or 1, with a deadzone
|
||||
mapping.from(xbox.LY).deadZone(0.5).constrainToInteger().to(standard.LY);
|
||||
mapping.join(standard.LB, standard.RB).pulse(0.5).to(actions.Yaw);
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "Enable Mapping"
|
||||
onClicked: NewControllers.enableMapping(root.mappingName)
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "Disable Mapping"
|
||||
onClicked: NewControllers.disableMapping(root.mappingName)
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing: 8
|
||||
Xbox { device: root.xbox }
|
||||
Xbox { device: root.standard }
|
||||
}
|
||||
|
||||
|
||||
Row {
|
||||
ScrollingGraph {
|
||||
controlId: NewControllers.Actions.Yaw
|
||||
label: "Yaw"
|
||||
min: -3.0
|
||||
max: 3.0
|
||||
size: 128
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
45
tests/controllers/qml/controls/AnalogButton.qml
Normal file
45
tests/controllers/qml/controls/AnalogButton.qml
Normal file
|
@ -0,0 +1,45 @@
|
|||
import QtQuick 2.1
|
||||
import QtQuick.Controls 1.0
|
||||
import QtQuick.Layouts 1.0
|
||||
import QtQuick.Dialogs 1.0
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property int size: 64
|
||||
width: size
|
||||
height: size
|
||||
property int controlId: 0
|
||||
property real value: 0
|
||||
property color color: 'black'
|
||||
|
||||
function update() {
|
||||
value = NewControllers.getValue(controlId);
|
||||
canvas.requestPaint();
|
||||
}
|
||||
|
||||
Timer {
|
||||
interval: 50; running: true; repeat: true
|
||||
onTriggered: root.update();
|
||||
}
|
||||
|
||||
Canvas {
|
||||
id: canvas
|
||||
anchors.fill: parent
|
||||
antialiasing: false
|
||||
|
||||
onPaint: {
|
||||
var ctx = canvas.getContext('2d');
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
var fillHeight = root.value * canvas.height;
|
||||
|
||||
ctx.fillStyle = 'red'
|
||||
ctx.fillRect(0, canvas.height - fillHeight, canvas.width, fillHeight);
|
||||
ctx.fill();
|
||||
ctx.restore()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
50
tests/controllers/qml/controls/AnalogStick.qml
Normal file
50
tests/controllers/qml/controls/AnalogStick.qml
Normal file
|
@ -0,0 +1,50 @@
|
|||
import QtQuick 2.1
|
||||
import QtQuick.Controls 1.0
|
||||
import QtQuick.Layouts 1.0
|
||||
import QtQuick.Dialogs 1.0
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property int size: 64
|
||||
width: size
|
||||
height: size
|
||||
|
||||
property int halfSize: size / 2
|
||||
property var controlIds: [ 0, 0 ]
|
||||
property vector2d value: Qt.vector2d(0, 0)
|
||||
|
||||
function update() {
|
||||
value = Qt.vector2d(
|
||||
NewControllers.getValue(controlIds[0]),
|
||||
NewControllers.getValue(controlIds[1])
|
||||
);
|
||||
canvas.requestPaint();
|
||||
}
|
||||
|
||||
Timer {
|
||||
interval: 50; running: true; repeat: true
|
||||
onTriggered: root.update()
|
||||
}
|
||||
|
||||
Canvas {
|
||||
id: canvas
|
||||
anchors.fill: parent
|
||||
antialiasing: false
|
||||
|
||||
onPaint: {
|
||||
var ctx = canvas.getContext('2d');
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.clearRect(0, 0, width, height);
|
||||
ctx.fill();
|
||||
ctx.translate(root.halfSize, root.halfSize)
|
||||
ctx.lineWidth = 4
|
||||
ctx.strokeStyle = Qt.rgba(Math.max(Math.abs(value.x), Math.abs(value.y)), 0, 0, 1)
|
||||
ctx.moveTo(0, 0).lineTo(root.value.x * root.halfSize, root.value.y * root.halfSize)
|
||||
ctx.stroke()
|
||||
ctx.restore()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
104
tests/controllers/qml/controls/ScrollingGraph.qml
Normal file
104
tests/controllers/qml/controls/ScrollingGraph.qml
Normal file
|
@ -0,0 +1,104 @@
|
|||
import QtQuick 2.1
|
||||
import QtQuick.Controls 1.0
|
||||
import QtQuick.Layouts 1.0
|
||||
import QtQuick.Dialogs 1.0
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property int size: 64
|
||||
width: size
|
||||
height: size
|
||||
|
||||
property int controlId: 0
|
||||
property real value: 0.5
|
||||
property int scrollWidth: 1
|
||||
property real min: 0.0
|
||||
property real max: 1.0
|
||||
property bool log: false
|
||||
property real range: max - min
|
||||
property color color: 'blue'
|
||||
property bool bar: false
|
||||
property real lastHeight: -1
|
||||
property string label: ""
|
||||
|
||||
function update() {
|
||||
value = NewControllers.getValue(controlId);
|
||||
canvas.requestPaint();
|
||||
}
|
||||
|
||||
function drawHeight() {
|
||||
if (value < min) {
|
||||
return 0;
|
||||
}
|
||||
if (value > max) {
|
||||
return height;
|
||||
}
|
||||
return ((value - min) / range) * height;
|
||||
}
|
||||
|
||||
Timer {
|
||||
interval: 50; running: true; repeat: true
|
||||
onTriggered: root.update()
|
||||
}
|
||||
|
||||
Canvas {
|
||||
id: canvas
|
||||
anchors.fill: parent
|
||||
antialiasing: false
|
||||
|
||||
Text {
|
||||
anchors.top: parent.top
|
||||
text: root.label
|
||||
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
text: root.max
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
text: root.min
|
||||
}
|
||||
|
||||
function scroll() {
|
||||
var ctx = canvas.getContext('2d');
|
||||
var image = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||
ctx.beginPath();
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
ctx.drawImage(image, -root.scrollWidth, 0, canvas.width, canvas.height)
|
||||
ctx.restore()
|
||||
}
|
||||
|
||||
onPaint: {
|
||||
scroll();
|
||||
var ctx = canvas.getContext('2d');
|
||||
ctx.save();
|
||||
var currentHeight = root.drawHeight();
|
||||
if (root.lastHeight == -1) {
|
||||
root.lastHeight = currentHeight
|
||||
}
|
||||
|
||||
// var x = canvas.width - root.drawWidth;
|
||||
// var y = canvas.height - drawHeight;
|
||||
// ctx.fillStyle = root.color
|
||||
// ctx.fillRect(x, y, root.drawWidth, root.bar ? drawHeight : 1)
|
||||
// ctx.fill();
|
||||
// ctx.restore()
|
||||
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.lineWidth = 1
|
||||
ctx.strokeStyle = root.color
|
||||
ctx.moveTo(canvas.width - root.scrollWidth, root.lastHeight).lineTo(canvas.width, currentHeight)
|
||||
ctx.stroke()
|
||||
ctx.restore()
|
||||
root.lastHeight = currentHeight
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
43
tests/controllers/qml/controls/ToggleButton.qml
Normal file
43
tests/controllers/qml/controls/ToggleButton.qml
Normal file
|
@ -0,0 +1,43 @@
|
|||
import QtQuick 2.1
|
||||
import QtQuick.Controls 1.0
|
||||
import QtQuick.Layouts 1.0
|
||||
import QtQuick.Dialogs 1.0
|
||||
|
||||
Item {
|
||||
id: root
|
||||
width: size
|
||||
height: size
|
||||
property int size: 64
|
||||
property int controlId: 0
|
||||
property real value: 0
|
||||
property color color: 'black'
|
||||
|
||||
Timer {
|
||||
interval: 50; running: true; repeat: true
|
||||
onTriggered: {
|
||||
root.value = NewControllers.getValue(root.controlId);
|
||||
canvas.requestPaint();
|
||||
}
|
||||
}
|
||||
|
||||
Canvas {
|
||||
id: canvas
|
||||
anchors.fill: parent
|
||||
antialiasing: false
|
||||
|
||||
onPaint: {
|
||||
var ctx = canvas.getContext('2d');
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.clearRect(0, 0, width, height);
|
||||
if (root.value > 0.0) {
|
||||
ctx.fillStyle = root.color
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
}
|
||||
ctx.fill();
|
||||
ctx.restore()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
43
tests/controllers/qml/xbox/DPad.qml
Normal file
43
tests/controllers/qml/xbox/DPad.qml
Normal file
|
@ -0,0 +1,43 @@
|
|||
import QtQuick 2.1
|
||||
import QtQuick.Controls 1.0
|
||||
import QtQuick.Layouts 1.0
|
||||
import QtQuick.Dialogs 1.0
|
||||
|
||||
import "./../controls"
|
||||
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property int size: 64
|
||||
width: size
|
||||
height: size
|
||||
property int spacer: size / 3
|
||||
property var device
|
||||
property color color: 'black'
|
||||
|
||||
ToggleButton {
|
||||
controlId: device.Up
|
||||
x: spacer
|
||||
width: spacer; height: spacer
|
||||
}
|
||||
|
||||
ToggleButton {
|
||||
controlId: device.Left
|
||||
y: spacer
|
||||
width: spacer; height: spacer
|
||||
}
|
||||
|
||||
ToggleButton {
|
||||
controlId: device.Right
|
||||
x: spacer * 2; y: spacer
|
||||
width: spacer; height: spacer
|
||||
}
|
||||
|
||||
ToggleButton {
|
||||
controlId: device.Down
|
||||
x: spacer; y: spacer * 2
|
||||
width: spacer; height: spacer
|
||||
}
|
||||
}
|
||||
|
||||
|
21
tests/controllers/qml/xbox/LeftAnalogStick.qml
Normal file
21
tests/controllers/qml/xbox/LeftAnalogStick.qml
Normal file
|
@ -0,0 +1,21 @@
|
|||
import QtQuick 2.1
|
||||
import QtQuick.Controls 1.0
|
||||
import QtQuick.Layouts 1.0
|
||||
import QtQuick.Dialogs 1.0
|
||||
|
||||
import "./../controls"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property int size: 64
|
||||
width: size
|
||||
height: size
|
||||
property var device
|
||||
|
||||
AnalogStick {
|
||||
size: size
|
||||
controlIds: [ device.LX, device.LY ]
|
||||
}
|
||||
}
|
||||
|
||||
|
21
tests/controllers/qml/xbox/RightAnalogStick.qml
Normal file
21
tests/controllers/qml/xbox/RightAnalogStick.qml
Normal file
|
@ -0,0 +1,21 @@
|
|||
import QtQuick 2.1
|
||||
import QtQuick.Controls 1.0
|
||||
import QtQuick.Layouts 1.0
|
||||
import QtQuick.Dialogs 1.0
|
||||
|
||||
import "./../controls"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property int size: 64
|
||||
width: size
|
||||
height: size
|
||||
property var device
|
||||
|
||||
AnalogStick {
|
||||
size: size
|
||||
controlIds: [ device.RX, device.RY ]
|
||||
}
|
||||
}
|
||||
|
||||
|
46
tests/controllers/qml/xbox/XboxButtons.qml
Normal file
46
tests/controllers/qml/xbox/XboxButtons.qml
Normal file
|
@ -0,0 +1,46 @@
|
|||
import QtQuick 2.1
|
||||
import QtQuick.Controls 1.0
|
||||
import QtQuick.Layouts 1.0
|
||||
import QtQuick.Dialogs 1.0
|
||||
|
||||
import "./../controls"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property int size: 64
|
||||
width: size
|
||||
height: size
|
||||
property int spacer: size / 3
|
||||
property var device
|
||||
property color color: 'black'
|
||||
|
||||
ToggleButton {
|
||||
controlId: device.Y
|
||||
x: spacer
|
||||
width: spacer; height: spacer
|
||||
color: 'yellow'
|
||||
}
|
||||
|
||||
ToggleButton {
|
||||
controlId: device.X
|
||||
y: spacer
|
||||
width: spacer; height: spacer
|
||||
color: 'blue'
|
||||
}
|
||||
|
||||
ToggleButton {
|
||||
controlId: device.B
|
||||
x: spacer * 2; y: spacer
|
||||
width: spacer; height: spacer
|
||||
color: 'red'
|
||||
}
|
||||
|
||||
ToggleButton {
|
||||
controlId: device.A
|
||||
x: spacer; y: spacer * 2
|
||||
width: spacer; height: spacer
|
||||
color: 'green'
|
||||
}
|
||||
}
|
||||
|
||||
|
BIN
tests/controllers/qml/xbox/xbox360-controller-md.png
Normal file
BIN
tests/controllers/qml/xbox/xbox360-controller-md.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 29 KiB |
|
@ -1,166 +1,116 @@
|
|||
//
|
||||
// main.cpp
|
||||
// tests/gpu-test/src
|
||||
//
|
||||
// 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 <unordered_map>
|
||||
#include <memory>
|
||||
#include <cstdio>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
#include <QtCore/QTime>
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QElapsedTimer>
|
||||
#include <QtCore/QFile>
|
||||
#include <QtCore/QLoggingCategory>
|
||||
#include <QtCore/QRegularExpression>
|
||||
|
||||
#include <QtGui/QResizeEvent>
|
||||
#include <QtGui/QWindow>
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtGui/QImage>
|
||||
|
||||
#include <QtQuick/QQuickItem>
|
||||
//
|
||||
// main.cpp
|
||||
// tests/gpu-test/src
|
||||
//
|
||||
// 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 <unordered_map>
|
||||
#include <memory>
|
||||
#include <cstdio>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
#include <QtCore/QTime>
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QElapsedTimer>
|
||||
#include <QtCore/QFile>
|
||||
#include <QtCore/QLoggingCategory>
|
||||
|
||||
#include <QtGui/QResizeEvent>
|
||||
#include <QtGui/QWindow>
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtGui/QImage>
|
||||
|
||||
#include <QtQuick/QQuickItem>
|
||||
#include <QtQml/QQmlApplicationEngine>
|
||||
#include <QtQml/QQmlContext>
|
||||
|
||||
#include <controllers/NewControllerScriptingInterface.h>
|
||||
#include <DependencyManager.h>
|
||||
#include <plugins/PluginManager.h>
|
||||
#include <input-plugins/InputPlugin.h>
|
||||
#include <input-plugins/KeyboardMouseDevice.h>
|
||||
#include <input-plugins/UserInputMapper.h>
|
||||
|
||||
const QString& getQmlDir() {
|
||||
static QString dir;
|
||||
if (dir.isEmpty()) {
|
||||
QDir path(__FILE__);
|
||||
path.cdUp();
|
||||
dir = path.cleanPath(path.absoluteFilePath("../qml/")) + "/";
|
||||
qDebug() << "Qml Path: " << dir;
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
|
||||
class AppHook : public QQuickItem {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
AppHook() {
|
||||
qDebug() << "Hook Created";
|
||||
}
|
||||
};
|
||||
|
||||
using namespace Controllers;
|
||||
|
||||
|
||||
QString sanatizeName(const QString& name) {
|
||||
QString cleanName{ name };
|
||||
cleanName.remove(QRegularExpression{ "[\\(\\)\\.\\s]" });
|
||||
return cleanName;
|
||||
}
|
||||
|
||||
const QVariantMap& getInputMap() {
|
||||
static std::once_flag once;
|
||||
static QVariantMap map;
|
||||
std::call_once(once, [&] {
|
||||
{
|
||||
QVariantMap hardwareMap;
|
||||
// Controller.Hardware.*
|
||||
auto devices = DependencyManager::get<UserInputMapper>()->getDevices();
|
||||
for (const auto& deviceMapping : devices) {
|
||||
auto device = deviceMapping.second.get();
|
||||
auto deviceName = sanatizeName(device->getName());
|
||||
auto deviceInputs = device->getAvailabeInputs();
|
||||
QVariantMap deviceMap;
|
||||
for (const auto& inputMapping : deviceInputs) {
|
||||
auto input = inputMapping.first;
|
||||
auto inputName = sanatizeName(inputMapping.second);
|
||||
deviceMap.insert(inputName, input.getID());
|
||||
}
|
||||
hardwareMap.insert(deviceName, deviceMap);
|
||||
}
|
||||
map.insert("Hardware", hardwareMap);
|
||||
}
|
||||
|
||||
// Controller.Actions.*
|
||||
{
|
||||
QVariantMap actionMap;
|
||||
auto actionNames = DependencyManager::get<UserInputMapper>()->getActionNames();
|
||||
int actionNumber = 0;
|
||||
for (const auto& actionName : actionNames) {
|
||||
actionMap.insert(sanatizeName(actionName), actionNumber++);
|
||||
}
|
||||
map.insert("Actions", actionMap);
|
||||
}
|
||||
});
|
||||
return map;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
DependencyManager::set<UserInputMapper>();
|
||||
PluginManager::getInstance()->getInputPlugins();
|
||||
|
||||
foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) {
|
||||
QString name = inputPlugin->getName();
|
||||
auto userInputMapper = DependencyManager::get<UserInputMapper>();
|
||||
if (name == KeyboardMouseDevice::NAME) {
|
||||
auto keyboardMouseDevice = static_cast<KeyboardMouseDevice*>(inputPlugin.data()); // TODO: this seems super hacky
|
||||
keyboardMouseDevice->registerToUserInputMapper(*userInputMapper);
|
||||
keyboardMouseDevice->assignDefaultInputMapping(*userInputMapper);
|
||||
}
|
||||
}
|
||||
|
||||
// Register our component type with QML.
|
||||
qmlRegisterType<AppHook>("com.highfidelity.test", 1, 0, "AppHook");
|
||||
//qmlRegisterType<NewControllerScriptingInterface>("com.highfidelity.test", 1, 0, "NewControllers");
|
||||
QGuiApplication app(argc, argv);
|
||||
|
||||
#include <plugins/Plugin.h>
|
||||
#include <plugins/PluginContainer.h>
|
||||
#include <plugins/PluginManager.h>
|
||||
#include <input-plugins/InputPlugin.h>
|
||||
#include <input-plugins/KeyboardMouseDevice.h>
|
||||
#include <controllers/NewControllerScriptingInterface.h>
|
||||
|
||||
#include <DependencyManager.h>
|
||||
#include <input-plugins/UserInputMapper.h>
|
||||
|
||||
const QString& getQmlDir() {
|
||||
static QString dir;
|
||||
if (dir.isEmpty()) {
|
||||
QDir path(__FILE__);
|
||||
path.cdUp();
|
||||
dir = path.cleanPath(path.absoluteFilePath("../qml/")) + "/";
|
||||
qDebug() << "Qml Path: " << dir;
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
|
||||
using namespace controller;
|
||||
|
||||
|
||||
class PluginContainerProxy : public QObject, PluginContainer {
|
||||
Q_OBJECT
|
||||
public:
|
||||
PluginContainerProxy() {
|
||||
Plugin::setContainer(this);
|
||||
}
|
||||
virtual ~PluginContainerProxy() {}
|
||||
virtual void addMenu(const QString& menuName) override {}
|
||||
virtual void removeMenu(const QString& menuName) override {}
|
||||
virtual QAction* addMenuItem(const QString& path, const QString& name, std::function<void(bool)> onClicked, bool checkable = false, bool checked = false, const QString& groupName = "") override { return nullptr; }
|
||||
virtual void removeMenuItem(const QString& menuName, const QString& menuItem) override {}
|
||||
virtual bool isOptionChecked(const QString& name) override { return false; }
|
||||
virtual void setIsOptionChecked(const QString& path, bool checked) override {}
|
||||
virtual void setFullscreen(const QScreen* targetScreen, bool hideMenu = true) override {}
|
||||
virtual void unsetFullscreen(const QScreen* avoidScreen = nullptr) override {}
|
||||
virtual void showDisplayPluginsTools() override {}
|
||||
virtual void requestReset() override {}
|
||||
virtual QGLWidget* getPrimarySurface() override { return nullptr; }
|
||||
virtual bool isForeground() override { return true; }
|
||||
virtual const DisplayPlugin* getActiveDisplayPlugin() const override { return nullptr; }
|
||||
};
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
QGuiApplication app(argc, argv);
|
||||
QQmlApplicationEngine engine;
|
||||
engine.rootContext()->setContextProperty("NewControllers", new NewControllerScriptingInterface());
|
||||
engine.rootContext()->setContextProperty("ControllerIds", getInputMap());
|
||||
engine.load(getQmlDir() + "main.qml");
|
||||
app.exec();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//QQmlEngine engine;
|
||||
//QQmlComponent *component = new QQmlComponent(&engine);
|
||||
//
|
||||
//QObject::connect(&engine, SIGNAL(quit()), QCoreApplication::instance(), SLOT(quit()));
|
||||
//
|
||||
//component->loadUrl(QUrl("main.qml"));
|
||||
//
|
||||
//if (!component->isReady()) {
|
||||
// qWarning("%s", qPrintable(component->errorString()));
|
||||
// return -1;
|
||||
//}
|
||||
//
|
||||
//QObject *topLevel = component->create();
|
||||
//QQuickWindow *window = qobject_cast<QQuickWindow *>(topLevel);
|
||||
//
|
||||
//QSurfaceFormat surfaceFormat = window->requestedFormat();
|
||||
//window->setFormat(surfaceFormat);
|
||||
//window->show();
|
||||
//
|
||||
//rc = app.exec();
|
||||
//
|
||||
//delete component;
|
||||
//return rc;
|
||||
//}
|
||||
|
||||
|
||||
|
||||
#include "main.moc"
|
||||
|
||||
|
||||
{
|
||||
DependencyManager::set<UserInputMapper>();
|
||||
foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) {
|
||||
QString name = inputPlugin->getName();
|
||||
inputPlugin->activate();
|
||||
auto userInputMapper = DependencyManager::get<UserInputMapper>();
|
||||
if (name == KeyboardMouseDevice::NAME) {
|
||||
auto keyboardMouseDevice = static_cast<KeyboardMouseDevice*>(inputPlugin.data()); // TODO: this seems super hacky
|
||||
keyboardMouseDevice->registerToUserInputMapper(*userInputMapper);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//new PluginContainerProxy();
|
||||
auto rootContext = engine.rootContext();
|
||||
|
||||
auto controllers = new NewControllerScriptingInterface();
|
||||
rootContext->setContextProperty("NewControllers", controllers);
|
||||
QVariantMap map;
|
||||
map.insert("Hardware", controllers->property("Hardware"));
|
||||
map.insert("Actions", controllers->property("Actions"));
|
||||
rootContext->setContextProperty("ControllerIds", map);
|
||||
}
|
||||
engine.load(getQmlDir() + "main.qml");
|
||||
app.exec();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include "main.moc"
|
||||
|
||||
|
|
Loading…
Reference in a new issue