Working on conditional and filter parsing

This commit is contained in:
Brad Davis 2015-10-21 14:31:22 -07:00
parent 58e5bff9a1
commit b9b03bd842
15 changed files with 453 additions and 211 deletions

View file

@ -1,10 +1,10 @@
{ {
"name": "Hydra to Standard", "name": "Hydra to Standard",
"channels": [ "channels": [
{ "from": "Hydra.LY", "to": "Standard.LY" }, { "from": "Hydra.LY", "filters": "invert", "to": "Standard.LY" },
{ "from": "Hydra.LX", "to": "Standard.LX" }, { "from": "Hydra.LX", "to": "Standard.LX" },
{ "from": "Hydra.LT", "to": "Standard.LT" }, { "from": "Hydra.LT", "to": "Standard.LT" },
{ "from": "Hydra.RY", "to": "Standard.RY" }, { "from": "Hydra.RY", "filters": "invert", "to": "Standard.RY" },
{ "from": "Hydra.RX", "to": "Standard.RX" }, { "from": "Hydra.RX", "to": "Standard.RX" },
{ "from": "Hydra.RT", "to": "Standard.RT" }, { "from": "Hydra.RT", "to": "Standard.RT" },

View file

@ -0,0 +1,43 @@
{
"name": "Standard to Action",
"channels": [
{ "from": "Standard.LY", "to": "Actions.TranslateZ" },
{ "from": "Standard.LX", "to": "Actions.TranslateX" },
{ "from": "Standard.RX", "to": "Actions.Yaw" },
{ "from": "Standard.RY", "to": "Actions.Pitch" },
{
"from": "Standard.DU",
"to": "Actions.LONGITUDINAL_FORWARD",
"filters": [ { "type": "scale", "scale": 0.5 } ]
},
{
"from": "Standard.DD",
"to": "Actions.LONGITUDINAL_BACKWARD",
"filters": [ { "type": "scale", "scale": 0.5 } ]
},
{
"from": "Standard.DR",
"to": "Actions.LATERAL_RIGHT",
"filters": [ { "type": "scale", "scale": 0.5 } ]
},
{
"from": "Standard.DL",
"to": "Actions.LATERAL_LEFT",
"filters": [ { "type": "scale", "scale": 0.5 } ]
},
{ "from": "Standard.Y", "to": "Actions.VERTICAL_UP" },
{ "from": "Standard.X", "to": "Actions.VERTICAL_DOWN" },
{
"from": "Standard.RT",
"to": "Actions.BOOM_IN",
"filters": [ { "type": "scale", "scale": 0.1 } ]
},
{
"from": "Standard.LT",
"to": "Actions.BOOM_OUT",
"filters": [ { "type": "scale", "scale": 0.1 } ]
},
{ "from": "Standard.LeftHand", "to": "Actions.LEFT_HAND" },
{ "from": "Standard.RightHand", "to": "Actions.RIGHT_HAND" }
]
}

View file

@ -5,38 +5,25 @@
{ "from": "Standard.LX", "to": "Actions.TranslateX" }, { "from": "Standard.LX", "to": "Actions.TranslateX" },
{ "from": "Standard.RX", "to": "Actions.Yaw" }, { "from": "Standard.RX", "to": "Actions.Yaw" },
{ "from": "Standard.RY", "to": "Actions.Pitch" }, { "from": "Standard.RY", "to": "Actions.Pitch" },
{
"from": "Standard.DU", { "from": [ "Standard.DU", "Standard.DU", "Standard.DU", "Standard.DD" ], "to": "Standard.LeftPrimaryThumb" },
"to": "Actions.LONGITUDINAL_FORWARD", { "from": "Standard.Back", "to": "Standard.LeftSecondaryThumb" },
"filters": [ { "type": "scale", "scale": 0.5 } ]
}, { "from": [ "Standard.A", "Standard.B", "Standard.X", "Standard.Y" ], "to": "Standard.RightPrimaryThumb" },
{ { "from": "Standard.Start", "to": "Standard.RightSecondaryThumb" },
"from": "Standard.DD",
"to": "Actions.LONGITUDINAL_BACKWARD",
"filters": [ { "type": "scale", "scale": 0.5 } ]
},
{
"from": "Standard.DR",
"to": "Actions.LATERAL_RIGHT",
"filters": [ { "type": "scale", "scale": 0.5 } ]
},
{
"from": "Standard.DL",
"to": "Actions.LATERAL_LEFT",
"filters": [ { "type": "scale", "scale": 0.5 } ]
},
{ "from": "Standard.Y", "to": "Actions.VERTICAL_UP" },
{ "from": "Standard.X", "to": "Actions.VERTICAL_DOWN" },
{ {
"from": "Standard.RT", "from": "Standard.RT",
"to": "Actions.BOOM_IN", "to": "Actions.BOOM_IN",
"filters": [ { "type": "scale", "scale": 0.1 } ] "filters": [ { "type": "scale", "scale": 0.1 } ]
}, },
{ {
"from": "Standard.LT", "from": "Standard.LT",
"to": "Actions.BOOM_OUT", "to": "Actions.BOOM_OUT",
"filters": [ { "type": "scale", "scale": 0.1 } ] "filters": [ { "type": "scale", "scale": 0.1 } ]
}, },
{ "from": "Standard.LeftHand", "to": "Actions.LEFT_HAND" }, { "from": "Standard.LeftHand", "to": "Actions.LEFT_HAND" },
{ "from": "Standard.RightHand", "to": "Actions.RIGHT_HAND" } { "from": "Standard.RightHand", "to": "Actions.RIGHT_HAND" }
] ]

View file

@ -183,32 +183,33 @@ HifiControls.VrDialog {
Xbox { device: root.xbox; label: "XBox"; width: 360 } Xbox { device: root.xbox; label: "XBox"; width: 360 }
Hydra { device: root.hydra; width: 360 } Hydra { device: root.hydra; width: 360 }
} }
// Row {
// spacing: 8 Row {
// ScrollingGraph { spacing: 8
// controlId: Controller.Actions.Yaw ScrollingGraph {
// label: "Yaw" controlId: Controller.Actions.Yaw
// min: -3.0 label: "Yaw"
// max: 3.0 min: -3.0
// size: 128 max: 3.0
// } size: 128
// }
// ScrollingGraph {
// controlId: Controller.Actions.YAW_LEFT ScrollingGraph {
// label: "Yaw Left" controlId: Controller.Actions.YAW_LEFT
// min: -3.0 label: "Yaw Left"
// max: 3.0 min: -3.0
// size: 128 max: 3.0
// } size: 128
// }
// ScrollingGraph {
// controlId: Controller.Actions.YAW_RIGHT ScrollingGraph {
// label: "Yaw Right" controlId: Controller.Actions.YAW_RIGHT
// min: -3.0 label: "Yaw Right"
// max: 3.0 min: -3.0
// size: 128 max: 3.0
// } size: 128
// } }
}
} }
} // dialog } // dialog

View file

@ -13,7 +13,7 @@ Item {
property color color: 'black' property color color: 'black'
function update() { function update() {
value = Controller.getValue(controlId); value = controlId ? Controller.getValue(controlId) : 0;
canvas.requestPaint(); canvas.requestPaint();
} }

View file

@ -17,8 +17,8 @@ Item {
function update() { function update() {
value = Qt.vector2d( value = Qt.vector2d(
Controller.getValue(controlIds[0]), controlIds[0] ? Controller.getValue(controlIds[0]) : 0,
Controller.getValue(controlIds[1]) controlIds[1] ? Controller.getValue(controlIds[1]) : 0
); );
if (root.invertY) { if (root.invertY) {
value.y = value.y * -1.0 value.y = value.y * -1.0
@ -27,7 +27,7 @@ Item {
} }
Timer { Timer {
interval: 50; running: true; repeat: true interval: 50; running: controlIds; repeat: true
onTriggered: root.update() onTriggered: root.update()
} }

View file

@ -12,12 +12,14 @@ Item {
property real value: 0 property real value: 0
property color color: 'black' property color color: 'black'
function update() {
value = controlId ? Controller.getValue(controlId) : 0;
canvas.requestPaint();
}
Timer { Timer {
interval: 50; running: true; repeat: true interval: 50; running: root.controlId; repeat: true
onTriggered: { onTriggered: root.update()
root.value = Controller.getValue(root.controlId);
canvas.requestPaint();
}
} }
Canvas { Canvas {

View file

@ -100,5 +100,20 @@ Item {
width: 16 * root.scale; height: 12 * root.scale width: 16 * root.scale; height: 12 * root.scale
x: (177 * root.scale); y: (45 * root.scale) x: (177 * root.scale); y: (45 * root.scale)
} }
// Left primary
ToggleButton {
x: 0; y: parent.height - height;
controlId: root.device.LeftPrimaryThumb
width: 16 * root.scale; height: 16 * root.scale
}
// Left primary
ToggleButton {
x: parent.width - width; y: parent.height - height;
controlId: root.device.RightPrimaryThumb
width: 16 * root.scale; height: 16 * root.scale
}
} }
} }

View file

@ -18,14 +18,4 @@ namespace controller {
return Conditional::Pointer(); return Conditional::Pointer();
} }
bool EndpointConditional::satisfied() {
if (!_endpoint) {
return false;
}
auto value = _endpoint->value();
if (value == 0.0f) {
return false;
}
return true;
}
} }

View file

@ -17,8 +17,6 @@
#include <shared/Factory.h> #include <shared/Factory.h>
#include "Endpoint.h"
class QJsonValue; class QJsonValue;
namespace controller { namespace controller {
@ -41,14 +39,6 @@ namespace controller {
static Factory _factory; static Factory _factory;
}; };
class EndpointConditional : public Conditional {
public:
EndpointConditional(Endpoint::Pointer endpoint) : _endpoint(endpoint) { }
virtual bool satisfied() override;
private:
Endpoint::Pointer _endpoint;
};
} }
#define REGISTER_CONDITIONAL_CLASS(classEntry) \ #define REGISTER_CONDITIONAL_CLASS(classEntry) \

View file

@ -40,9 +40,12 @@ namespace controller {
virtual void apply(float newValue, float oldValue, const Pointer& source) = 0; virtual void apply(float newValue, float oldValue, const Pointer& source) = 0;
virtual Pose pose() { return Pose(); } virtual Pose pose() { return Pose(); }
virtual void apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) {} virtual void apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) {}
virtual const bool isPose() { return _input.isPose(); } virtual const bool isPose() { return _input.isPose(); }
virtual bool writeable() const { return true; }
virtual bool readable() const { return true; }
virtual void reset() { }
const Input& getInput() { return _input; } const Input& getInput() { return _input; }
protected: protected:
@ -61,6 +64,26 @@ namespace controller {
ReadLambda _readLambda; ReadLambda _readLambda;
WriteLambda _writeLambda; WriteLambda _writeLambda;
}; };
class VirtualEndpoint : public Endpoint {
public:
VirtualEndpoint(const Input& id = Input::INVALID_INPUT)
: Endpoint(id) {
}
virtual float value() override { return _currentValue; }
virtual void apply(float newValue, float oldValue, const Pointer& source) override { _currentValue = newValue; }
virtual Pose pose() override { return _currentPose; }
virtual void apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) override {
_currentPose = newValue;
}
protected:
float _currentValue { 0.0f };
Pose _currentPose {};
};
} }
#endif #endif

View file

@ -55,6 +55,7 @@ struct Input {
Input(const Input& src) : id(src.id) {} Input(const Input& src) : id(src.id) {}
Input& operator = (const Input& src) { id = src.id; return (*this); } Input& operator = (const Input& src) { id = src.id; return (*this); }
bool operator ==(const Input& right) const { return INVALID_INPUT.id != id && INVALID_INPUT.id != right.id && id == right.id; } bool operator ==(const Input& right) const { return INVALID_INPUT.id != id && INVALID_INPUT.id != right.id && id == right.id; }
bool operator !=(const Input& right) const { return !(*this == right); }
bool operator < (const Input& src) const { return id < src.id; } bool operator < (const Input& src) const { return id < src.id; }
static const Input INVALID_INPUT; static const Input INVALID_INPUT;

View file

@ -30,6 +30,7 @@ namespace controller {
Pose(const Pose&) = default; Pose(const Pose&) = default;
Pose& operator = (const Pose&) = default; Pose& operator = (const Pose&) = default;
bool operator ==(const Pose& right) const; bool operator ==(const Pose& right) const;
bool operator !=(const Pose& right) const { return !(*this == right); }
bool isValid() const { return valid; } bool isValid() const { return valid; }
vec3 getTranslation() const { return translation; } vec3 getTranslation() const { return translation; }
quat getRotation() const { return rotation; } quat getRotation() const { return rotation; }

View file

@ -55,22 +55,45 @@ private:
float _lastValue = 0.0f; float _lastValue = 0.0f;
}; };
class VirtualEndpoint : public Endpoint { class StandardEndpoint : public VirtualEndpoint {
public: public:
VirtualEndpoint(const Input& id = Input::INVALID_INPUT) StandardEndpoint(const Input& input) : VirtualEndpoint(input) {}
: Endpoint(id) { virtual bool writeable() const override { return !_written; }
virtual bool readable() const override { return !_read; }
virtual void reset() override {
apply(0.0f, 0.0f, Endpoint::Pointer());
apply(Pose(), Pose(), Endpoint::Pointer());
_written = _read = false;
} }
virtual float value() override { return _currentValue; } virtual float value() override {
virtual void apply(float newValue, float oldValue, const Pointer& source) override { _currentValue = newValue; } _read = true;
return VirtualEndpoint::value();
}
virtual void apply(float newValue, float oldValue, const Pointer& source) override {
// For standard endpoints, the first NON-ZERO write counts.
if (newValue != 0.0) {
_written = true;
}
VirtualEndpoint::apply(newValue, oldValue, source);
}
virtual Pose pose() override {
_read = true;
return VirtualEndpoint::pose();
}
virtual Pose pose() override { return _currentPose; }
virtual void apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) override { virtual void apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) override {
_currentPose = newValue; if (newValue != Pose()) {
_written = true;
}
VirtualEndpoint::apply(newValue, oldValue, source);
} }
private: private:
float _currentValue{ 0.0f }; bool _written { false };
Pose _currentPose{}; bool _read { false };
}; };
@ -136,12 +159,67 @@ public:
virtual void apply(float newValue, float oldValue, const Pointer& source) { virtual void apply(float newValue, float oldValue, const Pointer& source) {
// Composites are read only // Composites are read only
} }
private:
Endpoint::Pointer _first;
Endpoint::Pointer _second;
}; };
class ArrayEndpoint : public Endpoint {
friend class UserInputMapper;
public:
using Pointer = std::shared_ptr<ArrayEndpoint>;
ArrayEndpoint() : Endpoint(Input::INVALID_INPUT) { }
virtual float value() override {
return 0.0;
}
virtual void apply(float newValue, float oldValue, const Endpoint::Pointer& source) override {
for (auto& child : _children) {
if (child->writeable()) {
child->apply(newValue, oldValue, source);
}
}
}
virtual bool readable() const override { return false; }
private:
Endpoint::List _children;
};
class AnyEndpoint : public Endpoint {
friend class UserInputMapper;
public:
using Pointer = std::shared_ptr<AnyEndpoint>;
AnyEndpoint() : Endpoint(Input::INVALID_INPUT) {}
virtual float value() override {
float result = 0;
for (auto& child : _children) {
float childResult = child->value();
if (childResult != 0.0f) {
result = childResult;
}
}
return result;
}
virtual void apply(float newValue, float oldValue, const Endpoint::Pointer& source) override {
qFatal("AnyEndpoint is read only");
}
virtual bool writeable() const override { return false; }
virtual bool readable() const override {
for (auto& child : _children) {
if (!child->readable()) {
return false;
}
}
return true;
}
private:
Endpoint::List _children;
};
class InputEndpoint : public Endpoint { class InputEndpoint : public Endpoint {
public: public:
@ -150,40 +228,44 @@ public:
} }
virtual float value() override { virtual float value() override {
_currentValue = 0.0f; _read = true;
if (isPose()) { if (isPose()) {
return _currentValue; return pose().valid ? 1.0f : 0.0f;
} }
auto userInputMapper = DependencyManager::get<UserInputMapper>(); auto userInputMapper = DependencyManager::get<UserInputMapper>();
auto deviceProxy = userInputMapper->getDeviceProxy(_input); auto deviceProxy = userInputMapper->getDeviceProxy(_input);
if (!deviceProxy) { if (!deviceProxy) {
return _currentValue; return 0.0f;
} }
_currentValue = deviceProxy->getValue(_input, 0); return deviceProxy->getValue(_input, 0);
return _currentValue;
} }
// FIXME need support for writing back to vibration / force feedback effects
virtual void apply(float newValue, float oldValue, const Pointer& source) override {} virtual void apply(float newValue, float oldValue, const Pointer& source) override {}
virtual Pose pose() override { virtual Pose pose() override {
_currentPose = Pose(); _read = true;
if (!isPose()) { if (!isPose()) {
return _currentPose; return Pose();
} }
auto userInputMapper = DependencyManager::get<UserInputMapper>(); auto userInputMapper = DependencyManager::get<UserInputMapper>();
auto deviceProxy = userInputMapper->getDeviceProxy(_input); auto deviceProxy = userInputMapper->getDeviceProxy(_input);
if (!deviceProxy) { if (!deviceProxy) {
return _currentPose; return Pose();
} }
_currentPose = deviceProxy->getPose(_input, 0); return deviceProxy->getPose(_input, 0);
return _currentPose;
} }
virtual void apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) override { virtual void apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) override { }
}
virtual bool writeable() const { return !_written; }
virtual bool readable() const { return !_read; }
virtual void reset() { _written = _read = false; }
private: private:
float _currentValue{ 0.0f };
Pose _currentPose{}; bool _written { false };
bool _read { false };
}; };
class ActionEndpoint : public Endpoint { class ActionEndpoint : public Endpoint {
@ -194,9 +276,8 @@ public:
virtual float value() override { return _currentValue; } virtual float value() override { return _currentValue; }
virtual void apply(float newValue, float oldValue, const Pointer& source) override { virtual void apply(float newValue, float oldValue, const Pointer& source) override {
_currentValue += newValue; _currentValue += newValue;
if (!(_input == Input::INVALID_INPUT)) { if (_input != Input::INVALID_INPUT) {
auto userInputMapper = DependencyManager::get<UserInputMapper>(); auto userInputMapper = DependencyManager::get<UserInputMapper>();
userInputMapper->deltaActionState(Action(_input.getChannel()), newValue); userInputMapper->deltaActionState(Action(_input.getChannel()), newValue);
} }
@ -208,12 +289,17 @@ public:
if (!_currentPose.isValid()) { if (!_currentPose.isValid()) {
return; return;
} }
if (!(_input == Input::INVALID_INPUT)) { if (_input != Input::INVALID_INPUT) {
auto userInputMapper = DependencyManager::get<UserInputMapper>(); auto userInputMapper = DependencyManager::get<UserInputMapper>();
userInputMapper->setActionState(Action(_input.getChannel()), _currentPose); userInputMapper->setActionState(Action(_input.getChannel()), _currentPose);
} }
} }
virtual void reset() override {
_currentValue = 0.0f;
_currentPose = Pose();
}
private: private:
float _currentValue{ 0.0f }; float _currentValue{ 0.0f };
Pose _currentPose{}; Pose _currentPose{};
@ -254,7 +340,7 @@ void UserInputMapper::registerDevice(InputDevice* device) {
} }
Endpoint::Pointer endpoint; Endpoint::Pointer endpoint;
if (input.device == STANDARD_DEVICE) { if (input.device == STANDARD_DEVICE) {
endpoint = std::make_shared<VirtualEndpoint>(input); endpoint = std::make_shared<StandardEndpoint>(input);
} else if (input.device == ACTIONS_DEVICE) { } else if (input.device == ACTIONS_DEVICE) {
endpoint = std::make_shared<ActionEndpoint>(input); endpoint = std::make_shared<ActionEndpoint>(input);
} else { } else {
@ -396,20 +482,7 @@ void UserInputMapper::update(float deltaTime) {
} }
// Run the mappings code // Run the mappings code
update(); runMappings();
// Scale all the channel step with the scale
for (auto i = 0; i < toInt(Action::NUM_ACTIONS); i++) {
if (_externalActionStates[i] != 0) {
_actionStates[i] += _externalActionStates[i];
_externalActionStates[i] = 0.0f;
}
if (_externalPoseStates[i].isValid()) {
_poseStates[i] = _externalPoseStates[i];
_externalPoseStates[i] = Pose();
}
}
// merge the bisected and non-bisected axes for now // merge the bisected and non-bisected axes for now
fixBisectedAxis(_actionStates[toInt(Action::TRANSLATE_X)], _actionStates[toInt(Action::LATERAL_LEFT)], _actionStates[toInt(Action::LATERAL_RIGHT)]); fixBisectedAxis(_actionStates[toInt(Action::TRANSLATE_X)], _actionStates[toInt(Action::LATERAL_LEFT)], _actionStates[toInt(Action::LATERAL_RIGHT)]);
@ -419,7 +492,6 @@ void UserInputMapper::update(float deltaTime) {
fixBisectedAxis(_actionStates[toInt(Action::ROTATE_Y)], _actionStates[toInt(Action::YAW_LEFT)], _actionStates[toInt(Action::YAW_RIGHT)]); fixBisectedAxis(_actionStates[toInt(Action::ROTATE_Y)], _actionStates[toInt(Action::YAW_LEFT)], _actionStates[toInt(Action::YAW_RIGHT)]);
fixBisectedAxis(_actionStates[toInt(Action::ROTATE_X)], _actionStates[toInt(Action::PITCH_UP)], _actionStates[toInt(Action::PITCH_DOWN)]); fixBisectedAxis(_actionStates[toInt(Action::ROTATE_X)], _actionStates[toInt(Action::PITCH_UP)], _actionStates[toInt(Action::PITCH_DOWN)]);
static const float EPSILON = 0.01f; static const float EPSILON = 0.01f;
for (auto i = 0; i < toInt(Action::NUM_ACTIONS); i++) { for (auto i = 0; i < toInt(Action::NUM_ACTIONS); i++) {
_actionStates[i] *= _actionScales[i]; _actionStates[i] *= _actionScales[i];
@ -561,53 +633,62 @@ Input UserInputMapper::makeStandardInput(controller::StandardPoseChannel pose) {
return Input(STANDARD_DEVICE, pose, ChannelType::POSE); return Input(STANDARD_DEVICE, pose, ChannelType::POSE);
} }
void UserInputMapper::update() { void UserInputMapper::runMappings() {
static auto deviceNames = getDeviceNames(); static auto deviceNames = getDeviceNames();
_overrideValues.clear(); _overrides.clear();
EndpointSet readEndpoints; for (auto endpointEntry : this->_endpointsByInput) {
EndpointSet writtenEndpoints; endpointEntry.second->reset();
}
// Now process the current values for each level of the stack // Now process the current values for each level of the stack
for (auto& mapping : _activeMappings) { for (auto& mapping : _activeMappings) {
for (const auto& route : mapping->routes) { for (const auto& route : mapping->routes) {
const auto& source = route->source;
// 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;
}
const auto& destination = route->destination;
// THis could happen if the route destination failed to create
// FIXME: Maybe do not create the route if the destination failed and avoid this case ?
if (!destination) {
continue;
}
if (writtenEndpoints.count(destination)) {
continue;
}
if (route->conditional) { if (route->conditional) {
if (!route->conditional->satisfied()) { if (!route->conditional->satisfied()) {
continue; continue;
} }
} }
// Standard controller destinations can only be can only be used once. auto source = route->source;
if (getStandardDeviceID() == destination->getInput().getDevice()) { if (_overrides.count(source)) {
writtenEndpoints.insert(destination); source = _overrides[source];
}
// 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 (!source->readable()) {
continue;
}
auto input = source->getInput();
float value = source->value();
if (value != 0.0) {
int i = 0;
}
auto destination = route->destination;
// THis could happen if the route destination failed to create
// FIXME: Maybe do not create the route if the destination failed and avoid this case ?
if (!destination) {
continue;
}
// FIXME?, should come before or after the override logic?
if (!destination->writeable()) {
continue;
} }
// Only consume the input if the route isn't a loopback. // Only consume the input if the route isn't a loopback.
// This allows mappings like `mapping.from(xbox.RY).invert().to(xbox.RY);` // This allows mappings like `mapping.from(xbox.RY).invert().to(xbox.RY);`
bool loopback = source == destination; bool loopback = (source->getInput() == destination->getInput()) && (source->getInput() != Input::INVALID_INPUT);
if (!loopback) { // Each time we loop back we re-write the override
readEndpoints.insert(source); if (loopback) {
_overrides[source] = destination = std::make_shared<StandardEndpoint>(source->getInput());
} }
// Fetch the value, may have been overriden by previous loopback routes // Fetch the value, may have been overriden by previous loopback routes
@ -624,14 +705,11 @@ void UserInputMapper::update() {
value = filter->apply(value); value = filter->apply(value);
} }
if (loopback) { destination->apply(value, 0, source);
_overrideValues[source] = value;
} else {
destination->apply(value, 0, source);
}
} }
} }
} }
} }
Endpoint::Pointer UserInputMapper::endpointFor(const QJSValue& endpoint) { Endpoint::Pointer UserInputMapper::endpointFor(const QJSValue& endpoint) {
@ -741,9 +819,9 @@ void UserInputMapper::enableMapping(const QString& mappingName, bool enable) {
} }
float UserInputMapper::getValue(const Endpoint::Pointer& endpoint) const { float UserInputMapper::getValue(const Endpoint::Pointer& endpoint) const {
auto valuesIterator = _overrideValues.find(endpoint); auto valuesIterator = _overrides.find(endpoint);
if (_overrideValues.end() != valuesIterator) { if (_overrides.end() != valuesIterator) {
return valuesIterator->second; return valuesIterator->second->value();
} }
return endpoint->value(); return endpoint->value();
@ -787,27 +865,70 @@ Mapping::Pointer UserInputMapper::loadMapping(const QString& jsonFile) {
return parseMapping(json); return parseMapping(json);
} }
static const QString JSON_NAME = QStringLiteral("name");
const QString JSON_NAME = QStringLiteral("name"); static const QString JSON_CHANNELS = QStringLiteral("channels");
const QString JSON_CHANNELS = QStringLiteral("channels"); static const QString JSON_CHANNEL_FROM = QStringLiteral("from");
const QString JSON_CHANNEL_FROM = QStringLiteral("from"); static const QString JSON_CHANNEL_WHEN = QStringLiteral("when");
const QString JSON_CHANNEL_WHEN = QStringLiteral("when"); static const QString JSON_CHANNEL_TO = QStringLiteral("to");
const QString JSON_CHANNEL_TO = QStringLiteral("to"); static const QString JSON_CHANNEL_FILTERS = QStringLiteral("filters");
const QString JSON_CHANNEL_FILTERS = QStringLiteral("filters");
Endpoint::Pointer UserInputMapper::parseEndpoint(const QJsonValue& value) { Endpoint::Pointer UserInputMapper::parseEndpoint(const QJsonValue& value) {
Endpoint::Pointer result;
if (value.isString()) { if (value.isString()) {
auto input = findDeviceInput(value.toString()); auto input = findDeviceInput(value.toString());
return endpointFor(input); result = endpointFor(input);
} else if (value.isObject()) { } else if (value.isObject()) {
// Endpoint is defined as an object, we expect a js function then // Endpoint is defined as an object, we expect a js function then
return Endpoint::Pointer(); return Endpoint::Pointer();
} }
return Endpoint::Pointer();
if (!result) {
qWarning() << "Invalid endpoint definition " << value;
}
return result;
} }
class AndConditional : public Conditional {
public:
using Pointer = std::shared_ptr<AndConditional>;
AndConditional(Conditional::List children) : _children(children) { }
virtual bool satisfied() override {
for (auto& conditional : _children) {
if (!conditional->satisfied()) {
return false;
}
}
return true;
}
private:
Conditional::List _children;
};
class EndpointConditional : public Conditional {
public:
EndpointConditional(Endpoint::Pointer endpoint) : _endpoint(endpoint) {}
virtual bool satisfied() override { return _endpoint && _endpoint->value() != 0.0; }
private:
Endpoint::Pointer _endpoint;
};
Conditional::Pointer UserInputMapper::parseConditional(const QJsonValue& value) { Conditional::Pointer UserInputMapper::parseConditional(const QJsonValue& value) {
if (value.isString()) { if (value.isArray()) {
// Support "when" : [ "GamePad.RB", "GamePad.LB" ]
Conditional::List children;
for (auto arrayItem : value.toArray()) {
Conditional::Pointer childConditional = parseConditional(arrayItem);
if (!childConditional) {
return Conditional::Pointer();
}
children.push_back(childConditional);
}
return std::make_shared<AndConditional>(children);
} else if (value.isString()) {
// Support "when" : "GamePad.RB"
auto input = findDeviceInput(value.toString()); auto input = findDeviceInput(value.toString());
auto endpoint = endpointFor(input); auto endpoint = endpointFor(input);
if (!endpoint) { if (!endpoint) {
@ -815,11 +936,85 @@ Conditional::Pointer UserInputMapper::parseConditional(const QJsonValue& value)
} }
return std::make_shared<EndpointConditional>(endpoint); return std::make_shared<EndpointConditional>(endpoint);
} }
return Conditional::parse(value); return Conditional::parse(value);
} }
Filter::Pointer UserInputMapper::parseFilter(const QJsonValue& value) {
Filter::Pointer result;
if (value.isString()) {
result = Filter::getFactory().create(value.toString());
} else if (value.isObject()) {
result = Filter::parse(value.toObject());
}
if (!result) {
qWarning() << "Invalid filter definition " << value;
}
return result;
}
Filter::List UserInputMapper::parseFilters(const QJsonValue& value) {
if (value.isNull()) {
return Filter::List();
}
if (value.isArray()) {
Filter::List result;
auto filtersArray = value.toArray();
for (auto filterValue : filtersArray) {
Filter::Pointer filter = parseFilter(filterValue);
if (!filter) {
return Filter::List();
}
result.push_back(filter);
}
return result;
}
Filter::Pointer filter = parseFilter(value);
if (!filter) {
return Filter::List();
}
return Filter::List({ filter });
}
Endpoint::Pointer UserInputMapper::parseDestination(const QJsonValue& value) {
if (value.isArray()) {
ArrayEndpoint::Pointer result = std::make_shared<ArrayEndpoint>();
for (auto arrayItem : value.toArray()) {
Endpoint::Pointer destination = parseEndpoint(arrayItem);
if (!destination) {
return Endpoint::Pointer();
}
result->_children.push_back(destination);
}
return result;
}
return parseEndpoint(value);
}
Endpoint::Pointer UserInputMapper::parseSource(const QJsonValue& value) {
if (value.isArray()) {
AnyEndpoint::Pointer result = std::make_shared<AnyEndpoint>();
for (auto arrayItem : value.toArray()) {
Endpoint::Pointer destination = parseEndpoint(arrayItem);
if (!destination) {
return Endpoint::Pointer();
}
result->_children.push_back(destination);
}
return result;
}
return parseEndpoint(value);
}
Route::Pointer UserInputMapper::parseRoute(const QJsonValue& value) { Route::Pointer UserInputMapper::parseRoute(const QJsonValue& value) {
if (!value.isObject()) { if (!value.isObject()) {
return Route::Pointer(); return Route::Pointer();
@ -827,47 +1022,37 @@ Route::Pointer UserInputMapper::parseRoute(const QJsonValue& value) {
const auto& obj = value.toObject(); const auto& obj = value.toObject();
Route::Pointer result = std::make_shared<Route>(); Route::Pointer result = std::make_shared<Route>();
result->source = parseEndpoint(obj[JSON_CHANNEL_FROM]); result->source = parseSource(obj[JSON_CHANNEL_FROM]);
if (!result->source) { if (!result->source) {
qWarning() << "Invalid route source " << obj[JSON_CHANNEL_FROM]; qWarning() << "Invalid route source " << obj[JSON_CHANNEL_FROM];
return Route::Pointer(); return Route::Pointer();
} }
result->destination = parseEndpoint(obj[JSON_CHANNEL_TO]);
result->destination = parseDestination(obj[JSON_CHANNEL_TO]);
if (!result->destination) { if (!result->destination) {
qWarning() << "Invalid route destination " << obj[JSON_CHANNEL_TO]; qWarning() << "Invalid route destination " << obj[JSON_CHANNEL_TO];
return Route::Pointer(); return Route::Pointer();
} }
if (obj.contains(JSON_CHANNEL_WHEN)) { if (obj.contains(JSON_CHANNEL_WHEN)) {
auto when = parseConditional(obj[JSON_CHANNEL_WHEN]); auto conditionalsValue = obj[JSON_CHANNEL_WHEN];
if (!when) { result->conditional = parseConditional(conditionalsValue);
qWarning() << "Invalid route conditional " << obj[JSON_CHANNEL_TO]; if (!result->conditional) {
qWarning() << "Invalid route conditionals " << conditionalsValue;
return Route::Pointer(); return Route::Pointer();
} }
result->conditional = when;
} }
const auto& filtersValue = obj[JSON_CHANNEL_FILTERS]; if (obj.contains(JSON_CHANNEL_FILTERS)) {
// FIXME support strings for filters with no parameters, both in the array and at the top level... auto filtersValue = obj[JSON_CHANNEL_FILTERS];
// i.e. result->filters = parseFilters(filtersValue);
// { "from": "Standard.DU", "to" : "Actions.LONGITUDINAL_FORWARD", "filters" : "invert" }, if (result->filters.empty()) {
// and qWarning() << "Invalid route filters " << filtersValue;
// { "from": "Standard.DU", "to" : "Actions.LONGITUDINAL_FORWARD", "filters" : [ "invert", "constrainToInteger" ] }, return Route::Pointer();
if (filtersValue.isArray()) {
auto filtersArray = filtersValue.toArray();
for (auto filterValue : filtersArray) {
if (!filterValue.isObject()) {
qWarning() << "Invalid filter " << filterValue;
return Route::Pointer();
}
Filter::Pointer filter = Filter::parse(filterValue.toObject());
if (!filter) {
qWarning() << "Invalid filter " << filterValue;
return Route::Pointer();
}
result->filters.push_back(filter);
} }
} }
return result; return result;
} }

View file

@ -50,7 +50,7 @@ namespace controller {
using MappingStack = std::list<Mapping::Pointer>; using MappingStack = std::list<Mapping::Pointer>;
using InputToEndpointMap = std::map<Input, Endpoint::Pointer>; using InputToEndpointMap = std::map<Input, Endpoint::Pointer>;
using EndpointSet = std::unordered_set<Endpoint::Pointer>; using EndpointSet = std::unordered_set<Endpoint::Pointer>;
using ValueMap = std::map<Endpoint::Pointer, float>; using EndpointOverrideMap = std::map<Endpoint::Pointer, Endpoint::Pointer>;
using EndpointPair = std::pair<Endpoint::Pointer, Endpoint::Pointer>; using EndpointPair = std::pair<Endpoint::Pointer, Endpoint::Pointer>;
using EndpointPairMap = std::map<EndpointPair, Endpoint::Pointer>; using EndpointPairMap = std::map<EndpointPair, Endpoint::Pointer>;
using DevicesMap = std::map<int, DeviceProxy::Pointer>; using DevicesMap = std::map<int, DeviceProxy::Pointer>;
@ -86,9 +86,9 @@ namespace controller {
int findAction(const QString& actionName) const; int findAction(const QString& actionName) const;
QVector<QString> getActionNames() const; QVector<QString> getActionNames() const;
void setActionState(Action action, float value) { _externalActionStates[toInt(action)] = value; } void setActionState(Action action, float value) { _actionStates[toInt(action)] = value; }
void deltaActionState(Action action, float delta) { _externalActionStates[toInt(action)] += delta; } void deltaActionState(Action action, float delta) { _actionStates[toInt(action)] += delta; }
void setActionState(Action action, const Pose& value) { _externalPoseStates[toInt(action)] = value; } void setActionState(Action action, const Pose& value) { _poseStates[toInt(action)] = value; }
static Input makeStandardInput(controller::StandardButtonChannel button); static Input makeStandardInput(controller::StandardButtonChannel button);
static Input makeStandardInput(controller::StandardAxisChannel axis); static Input makeStandardInput(controller::StandardAxisChannel axis);
@ -119,7 +119,7 @@ namespace controller {
void hardwareChanged(); void hardwareChanged();
protected: protected:
virtual void update(); virtual void runMappings();
// GetFreeDeviceID should be called before registering a device to use an ID not used by a different device. // GetFreeDeviceID should be called before registering a device to use an ID not used by a different device.
uint16 getFreeDeviceID() { return _nextFreeDeviceID++; } uint16 getFreeDeviceID() { return _nextFreeDeviceID++; }
@ -128,11 +128,9 @@ namespace controller {
uint16 _nextFreeDeviceID = STANDARD_DEVICE + 1; uint16 _nextFreeDeviceID = STANDARD_DEVICE + 1;
std::vector<float> _actionStates = std::vector<float>(toInt(Action::NUM_ACTIONS), 0.0f); std::vector<float> _actionStates = std::vector<float>(toInt(Action::NUM_ACTIONS), 0.0f);
std::vector<float> _externalActionStates = std::vector<float>(toInt(Action::NUM_ACTIONS), 0.0f);
std::vector<float> _actionScales = std::vector<float>(toInt(Action::NUM_ACTIONS), 1.0f); std::vector<float> _actionScales = std::vector<float>(toInt(Action::NUM_ACTIONS), 1.0f);
std::vector<float> _lastActionStates = std::vector<float>(toInt(Action::NUM_ACTIONS), 0.0f); std::vector<float> _lastActionStates = std::vector<float>(toInt(Action::NUM_ACTIONS), 0.0f);
std::vector<Pose> _poseStates = std::vector<Pose>(toInt(Action::NUM_ACTIONS)); std::vector<Pose> _poseStates = std::vector<Pose>(toInt(Action::NUM_ACTIONS));
std::vector<Pose> _externalPoseStates = std::vector<Pose>(toInt(Action::NUM_ACTIONS));
glm::mat4 _sensorToWorldMat; glm::mat4 _sensorToWorldMat;
@ -148,16 +146,22 @@ namespace controller {
Endpoint::Pointer endpointFor(const QScriptValue& endpoint); Endpoint::Pointer endpointFor(const QScriptValue& endpoint);
Endpoint::Pointer endpointFor(const Input& endpoint) const; Endpoint::Pointer endpointFor(const Input& endpoint) const;
Endpoint::Pointer compositeEndpointFor(Endpoint::Pointer first, Endpoint::Pointer second); Endpoint::Pointer compositeEndpointFor(Endpoint::Pointer first, Endpoint::Pointer second);
Mapping::Pointer parseMapping(const QJsonValue& json); Mapping::Pointer parseMapping(const QJsonValue& json);
Route::Pointer parseRoute(const QJsonValue& value); Route::Pointer parseRoute(const QJsonValue& value);
Conditional::Pointer parseConditional(const QJsonValue& value); Endpoint::Pointer parseDestination(const QJsonValue& value);
Endpoint::Pointer parseSource(const QJsonValue& value);
Endpoint::Pointer parseEndpoint(const QJsonValue& value); Endpoint::Pointer parseEndpoint(const QJsonValue& value);
Conditional::Pointer parseConditional(const QJsonValue& value);
static Filter::Pointer parseFilter(const QJsonValue& value);
static Filter::List parseFilters(const QJsonValue& value);
InputToEndpointMap _endpointsByInput; InputToEndpointMap _endpointsByInput;
EndpointToInputMap _inputsByEndpoint; EndpointToInputMap _inputsByEndpoint;
EndpointPairMap _compositeEndpoints; EndpointPairMap _compositeEndpoints;
ValueMap _overrideValues; EndpointOverrideMap _overrides;
MappingNameMap _mappingsByName; MappingNameMap _mappingsByName;
Mapping::Pointer _defaultMapping{ std::make_shared<Mapping>("Default") }; Mapping::Pointer _defaultMapping{ std::make_shared<Mapping>("Default") };
MappingDeviceMap _mappingsByDevice; MappingDeviceMap _mappingsByDevice;