mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-13 22:27:13 +02:00
Working on conditional and filter parsing
This commit is contained in:
parent
58e5bff9a1
commit
b9b03bd842
15 changed files with 453 additions and 211 deletions
|
@ -1,10 +1,10 @@
|
|||
{
|
||||
"name": "Hydra to Standard",
|
||||
"channels": [
|
||||
{ "from": "Hydra.LY", "to": "Standard.LY" },
|
||||
{ "from": "Hydra.LY", "filters": "invert", "to": "Standard.LY" },
|
||||
{ "from": "Hydra.LX", "to": "Standard.LX" },
|
||||
{ "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.RT", "to": "Standard.RT" },
|
||||
|
||||
|
|
43
interface/resources/controllers/standard-old.json
Normal file
43
interface/resources/controllers/standard-old.json
Normal 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" }
|
||||
]
|
||||
}
|
|
@ -5,38 +5,25 @@
|
|||
{ "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.DU", "Standard.DU", "Standard.DU", "Standard.DD" ], "to": "Standard.LeftPrimaryThumb" },
|
||||
{ "from": "Standard.Back", "to": "Standard.LeftSecondaryThumb" },
|
||||
|
||||
{ "from": [ "Standard.A", "Standard.B", "Standard.X", "Standard.Y" ], "to": "Standard.RightPrimaryThumb" },
|
||||
{ "from": "Standard.Start", "to": "Standard.RightSecondaryThumb" },
|
||||
|
||||
{
|
||||
"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" }
|
||||
]
|
||||
|
|
|
@ -183,32 +183,33 @@ HifiControls.VrDialog {
|
|||
Xbox { device: root.xbox; label: "XBox"; width: 360 }
|
||||
Hydra { device: root.hydra; width: 360 }
|
||||
}
|
||||
// Row {
|
||||
// spacing: 8
|
||||
// ScrollingGraph {
|
||||
// controlId: Controller.Actions.Yaw
|
||||
// label: "Yaw"
|
||||
// min: -3.0
|
||||
// max: 3.0
|
||||
// size: 128
|
||||
// }
|
||||
//
|
||||
// ScrollingGraph {
|
||||
// controlId: Controller.Actions.YAW_LEFT
|
||||
// label: "Yaw Left"
|
||||
// min: -3.0
|
||||
// max: 3.0
|
||||
// size: 128
|
||||
// }
|
||||
//
|
||||
// ScrollingGraph {
|
||||
// controlId: Controller.Actions.YAW_RIGHT
|
||||
// label: "Yaw Right"
|
||||
// min: -3.0
|
||||
// max: 3.0
|
||||
// size: 128
|
||||
// }
|
||||
// }
|
||||
|
||||
Row {
|
||||
spacing: 8
|
||||
ScrollingGraph {
|
||||
controlId: Controller.Actions.Yaw
|
||||
label: "Yaw"
|
||||
min: -3.0
|
||||
max: 3.0
|
||||
size: 128
|
||||
}
|
||||
|
||||
ScrollingGraph {
|
||||
controlId: Controller.Actions.YAW_LEFT
|
||||
label: "Yaw Left"
|
||||
min: -3.0
|
||||
max: 3.0
|
||||
size: 128
|
||||
}
|
||||
|
||||
ScrollingGraph {
|
||||
controlId: Controller.Actions.YAW_RIGHT
|
||||
label: "Yaw Right"
|
||||
min: -3.0
|
||||
max: 3.0
|
||||
size: 128
|
||||
}
|
||||
}
|
||||
}
|
||||
} // dialog
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ Item {
|
|||
property color color: 'black'
|
||||
|
||||
function update() {
|
||||
value = Controller.getValue(controlId);
|
||||
value = controlId ? Controller.getValue(controlId) : 0;
|
||||
canvas.requestPaint();
|
||||
}
|
||||
|
||||
|
|
|
@ -17,8 +17,8 @@ Item {
|
|||
|
||||
function update() {
|
||||
value = Qt.vector2d(
|
||||
Controller.getValue(controlIds[0]),
|
||||
Controller.getValue(controlIds[1])
|
||||
controlIds[0] ? Controller.getValue(controlIds[0]) : 0,
|
||||
controlIds[1] ? Controller.getValue(controlIds[1]) : 0
|
||||
);
|
||||
if (root.invertY) {
|
||||
value.y = value.y * -1.0
|
||||
|
@ -27,7 +27,7 @@ Item {
|
|||
}
|
||||
|
||||
Timer {
|
||||
interval: 50; running: true; repeat: true
|
||||
interval: 50; running: controlIds; repeat: true
|
||||
onTriggered: root.update()
|
||||
}
|
||||
|
||||
|
|
|
@ -12,12 +12,14 @@ Item {
|
|||
property real value: 0
|
||||
property color color: 'black'
|
||||
|
||||
function update() {
|
||||
value = controlId ? Controller.getValue(controlId) : 0;
|
||||
canvas.requestPaint();
|
||||
}
|
||||
|
||||
Timer {
|
||||
interval: 50; running: true; repeat: true
|
||||
onTriggered: {
|
||||
root.value = Controller.getValue(root.controlId);
|
||||
canvas.requestPaint();
|
||||
}
|
||||
interval: 50; running: root.controlId; repeat: true
|
||||
onTriggered: root.update()
|
||||
}
|
||||
|
||||
Canvas {
|
||||
|
|
|
@ -100,5 +100,20 @@ Item {
|
|||
width: 16 * root.scale; height: 12 * 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
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,14 +18,4 @@ namespace controller {
|
|||
return Conditional::Pointer();
|
||||
}
|
||||
|
||||
bool EndpointConditional::satisfied() {
|
||||
if (!_endpoint) {
|
||||
return false;
|
||||
}
|
||||
auto value = _endpoint->value();
|
||||
if (value == 0.0f) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,8 +17,6 @@
|
|||
|
||||
#include <shared/Factory.h>
|
||||
|
||||
#include "Endpoint.h"
|
||||
|
||||
class QJsonValue;
|
||||
|
||||
namespace controller {
|
||||
|
@ -41,14 +39,6 @@ namespace controller {
|
|||
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) \
|
||||
|
|
|
@ -40,9 +40,12 @@ namespace controller {
|
|||
virtual void apply(float newValue, float oldValue, const Pointer& source) = 0;
|
||||
virtual Pose pose() { return Pose(); }
|
||||
virtual void apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) {}
|
||||
|
||||
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; }
|
||||
|
||||
protected:
|
||||
|
@ -61,6 +64,26 @@ namespace controller {
|
|||
ReadLambda _readLambda;
|
||||
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
|
||||
|
|
|
@ -55,6 +55,7 @@ struct Input {
|
|||
Input(const Input& src) : id(src.id) {}
|
||||
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 !(*this == right); }
|
||||
bool operator < (const Input& src) const { return id < src.id; }
|
||||
|
||||
static const Input INVALID_INPUT;
|
||||
|
|
|
@ -30,6 +30,7 @@ namespace controller {
|
|||
Pose(const Pose&) = default;
|
||||
Pose& operator = (const Pose&) = default;
|
||||
bool operator ==(const Pose& right) const;
|
||||
bool operator !=(const Pose& right) const { return !(*this == right); }
|
||||
bool isValid() const { return valid; }
|
||||
vec3 getTranslation() const { return translation; }
|
||||
quat getRotation() const { return rotation; }
|
||||
|
|
|
@ -55,22 +55,45 @@ private:
|
|||
float _lastValue = 0.0f;
|
||||
};
|
||||
|
||||
class VirtualEndpoint : public Endpoint {
|
||||
class StandardEndpoint : public VirtualEndpoint {
|
||||
public:
|
||||
VirtualEndpoint(const Input& id = Input::INVALID_INPUT)
|
||||
: Endpoint(id) {
|
||||
StandardEndpoint(const Input& input) : VirtualEndpoint(input) {}
|
||||
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 void apply(float newValue, float oldValue, const Pointer& source) override { _currentValue = newValue; }
|
||||
virtual float value() override {
|
||||
_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 {
|
||||
_currentPose = newValue;
|
||||
if (newValue != Pose()) {
|
||||
_written = true;
|
||||
}
|
||||
VirtualEndpoint::apply(newValue, oldValue, source);
|
||||
}
|
||||
|
||||
private:
|
||||
float _currentValue{ 0.0f };
|
||||
Pose _currentPose{};
|
||||
bool _written { false };
|
||||
bool _read { false };
|
||||
};
|
||||
|
||||
|
||||
|
@ -136,12 +159,67 @@ public:
|
|||
virtual void apply(float newValue, float oldValue, const Pointer& source) {
|
||||
// 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 {
|
||||
public:
|
||||
|
@ -150,40 +228,44 @@ public:
|
|||
}
|
||||
|
||||
virtual float value() override {
|
||||
_currentValue = 0.0f;
|
||||
_read = true;
|
||||
if (isPose()) {
|
||||
return _currentValue;
|
||||
return pose().valid ? 1.0f : 0.0f;
|
||||
}
|
||||
auto userInputMapper = DependencyManager::get<UserInputMapper>();
|
||||
auto deviceProxy = userInputMapper->getDeviceProxy(_input);
|
||||
if (!deviceProxy) {
|
||||
return _currentValue;
|
||||
return 0.0f;
|
||||
}
|
||||
_currentValue = deviceProxy->getValue(_input, 0);
|
||||
return _currentValue;
|
||||
return deviceProxy->getValue(_input, 0);
|
||||
}
|
||||
|
||||
// FIXME need support for writing back to vibration / force feedback effects
|
||||
virtual void apply(float newValue, float oldValue, const Pointer& source) override {}
|
||||
|
||||
virtual Pose pose() override {
|
||||
_currentPose = Pose();
|
||||
_read = true;
|
||||
if (!isPose()) {
|
||||
return _currentPose;
|
||||
return Pose();
|
||||
}
|
||||
auto userInputMapper = DependencyManager::get<UserInputMapper>();
|
||||
auto deviceProxy = userInputMapper->getDeviceProxy(_input);
|
||||
if (!deviceProxy) {
|
||||
return _currentPose;
|
||||
return Pose();
|
||||
}
|
||||
_currentPose = deviceProxy->getPose(_input, 0);
|
||||
return _currentPose;
|
||||
return deviceProxy->getPose(_input, 0);
|
||||
}
|
||||
|
||||
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:
|
||||
float _currentValue{ 0.0f };
|
||||
Pose _currentPose{};
|
||||
|
||||
bool _written { false };
|
||||
bool _read { false };
|
||||
};
|
||||
|
||||
class ActionEndpoint : public Endpoint {
|
||||
|
@ -194,9 +276,8 @@ public:
|
|||
|
||||
virtual float value() override { return _currentValue; }
|
||||
virtual void apply(float newValue, float oldValue, const Pointer& source) override {
|
||||
|
||||
_currentValue += newValue;
|
||||
if (!(_input == Input::INVALID_INPUT)) {
|
||||
if (_input != Input::INVALID_INPUT) {
|
||||
auto userInputMapper = DependencyManager::get<UserInputMapper>();
|
||||
userInputMapper->deltaActionState(Action(_input.getChannel()), newValue);
|
||||
}
|
||||
|
@ -208,12 +289,17 @@ public:
|
|||
if (!_currentPose.isValid()) {
|
||||
return;
|
||||
}
|
||||
if (!(_input == Input::INVALID_INPUT)) {
|
||||
if (_input != Input::INVALID_INPUT) {
|
||||
auto userInputMapper = DependencyManager::get<UserInputMapper>();
|
||||
userInputMapper->setActionState(Action(_input.getChannel()), _currentPose);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void reset() override {
|
||||
_currentValue = 0.0f;
|
||||
_currentPose = Pose();
|
||||
}
|
||||
|
||||
private:
|
||||
float _currentValue{ 0.0f };
|
||||
Pose _currentPose{};
|
||||
|
@ -254,7 +340,7 @@ void UserInputMapper::registerDevice(InputDevice* device) {
|
|||
}
|
||||
Endpoint::Pointer endpoint;
|
||||
if (input.device == STANDARD_DEVICE) {
|
||||
endpoint = std::make_shared<VirtualEndpoint>(input);
|
||||
endpoint = std::make_shared<StandardEndpoint>(input);
|
||||
} else if (input.device == ACTIONS_DEVICE) {
|
||||
endpoint = std::make_shared<ActionEndpoint>(input);
|
||||
} else {
|
||||
|
@ -396,20 +482,7 @@ void UserInputMapper::update(float deltaTime) {
|
|||
}
|
||||
|
||||
// Run the mappings code
|
||||
update();
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
runMappings();
|
||||
|
||||
// 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)]);
|
||||
|
@ -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_X)], _actionStates[toInt(Action::PITCH_UP)], _actionStates[toInt(Action::PITCH_DOWN)]);
|
||||
|
||||
|
||||
static const float EPSILON = 0.01f;
|
||||
for (auto i = 0; i < toInt(Action::NUM_ACTIONS); i++) {
|
||||
_actionStates[i] *= _actionScales[i];
|
||||
|
@ -561,53 +633,62 @@ Input UserInputMapper::makeStandardInput(controller::StandardPoseChannel pose) {
|
|||
return Input(STANDARD_DEVICE, pose, ChannelType::POSE);
|
||||
}
|
||||
|
||||
void UserInputMapper::update() {
|
||||
void UserInputMapper::runMappings() {
|
||||
static auto deviceNames = getDeviceNames();
|
||||
_overrideValues.clear();
|
||||
_overrides.clear();
|
||||
|
||||
EndpointSet readEndpoints;
|
||||
EndpointSet writtenEndpoints;
|
||||
for (auto endpointEntry : this->_endpointsByInput) {
|
||||
endpointEntry.second->reset();
|
||||
}
|
||||
|
||||
// Now process the current values for each level of the stack
|
||||
for (auto& mapping : _activeMappings) {
|
||||
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->satisfied()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Standard controller destinations can only be can only be used once.
|
||||
if (getStandardDeviceID() == destination->getInput().getDevice()) {
|
||||
writtenEndpoints.insert(destination);
|
||||
auto source = route->source;
|
||||
if (_overrides.count(source)) {
|
||||
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.
|
||||
// This allows mappings like `mapping.from(xbox.RY).invert().to(xbox.RY);`
|
||||
bool loopback = source == destination;
|
||||
if (!loopback) {
|
||||
readEndpoints.insert(source);
|
||||
bool loopback = (source->getInput() == destination->getInput()) && (source->getInput() != Input::INVALID_INPUT);
|
||||
// Each time we loop back we re-write the override
|
||||
if (loopback) {
|
||||
_overrides[source] = destination = std::make_shared<StandardEndpoint>(source->getInput());
|
||||
}
|
||||
|
||||
// Fetch the value, may have been overriden by previous loopback routes
|
||||
|
@ -624,14 +705,11 @@ void UserInputMapper::update() {
|
|||
value = filter->apply(value);
|
||||
}
|
||||
|
||||
if (loopback) {
|
||||
_overrideValues[source] = value;
|
||||
} else {
|
||||
destination->apply(value, 0, source);
|
||||
}
|
||||
destination->apply(value, 0, source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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 {
|
||||
auto valuesIterator = _overrideValues.find(endpoint);
|
||||
if (_overrideValues.end() != valuesIterator) {
|
||||
return valuesIterator->second;
|
||||
auto valuesIterator = _overrides.find(endpoint);
|
||||
if (_overrides.end() != valuesIterator) {
|
||||
return valuesIterator->second->value();
|
||||
}
|
||||
|
||||
return endpoint->value();
|
||||
|
@ -787,27 +865,70 @@ Mapping::Pointer UserInputMapper::loadMapping(const QString& jsonFile) {
|
|||
return parseMapping(json);
|
||||
}
|
||||
|
||||
|
||||
const QString JSON_NAME = QStringLiteral("name");
|
||||
const QString JSON_CHANNELS = QStringLiteral("channels");
|
||||
const QString JSON_CHANNEL_FROM = QStringLiteral("from");
|
||||
const QString JSON_CHANNEL_WHEN = QStringLiteral("when");
|
||||
const QString JSON_CHANNEL_TO = QStringLiteral("to");
|
||||
const QString JSON_CHANNEL_FILTERS = QStringLiteral("filters");
|
||||
static const QString JSON_NAME = QStringLiteral("name");
|
||||
static const QString JSON_CHANNELS = QStringLiteral("channels");
|
||||
static const QString JSON_CHANNEL_FROM = QStringLiteral("from");
|
||||
static const QString JSON_CHANNEL_WHEN = QStringLiteral("when");
|
||||
static const QString JSON_CHANNEL_TO = QStringLiteral("to");
|
||||
static const QString JSON_CHANNEL_FILTERS = QStringLiteral("filters");
|
||||
|
||||
Endpoint::Pointer UserInputMapper::parseEndpoint(const QJsonValue& value) {
|
||||
Endpoint::Pointer result;
|
||||
if (value.isString()) {
|
||||
auto input = findDeviceInput(value.toString());
|
||||
return endpointFor(input);
|
||||
result = endpointFor(input);
|
||||
} else if (value.isObject()) {
|
||||
// Endpoint is defined as an object, we expect a js function then
|
||||
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) {
|
||||
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 endpoint = endpointFor(input);
|
||||
if (!endpoint) {
|
||||
|
@ -815,11 +936,85 @@ Conditional::Pointer UserInputMapper::parseConditional(const QJsonValue& value)
|
|||
}
|
||||
|
||||
return std::make_shared<EndpointConditional>(endpoint);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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) {
|
||||
if (!value.isObject()) {
|
||||
return Route::Pointer();
|
||||
|
@ -827,47 +1022,37 @@ Route::Pointer UserInputMapper::parseRoute(const QJsonValue& value) {
|
|||
|
||||
const auto& obj = value.toObject();
|
||||
Route::Pointer result = std::make_shared<Route>();
|
||||
result->source = parseEndpoint(obj[JSON_CHANNEL_FROM]);
|
||||
result->source = parseSource(obj[JSON_CHANNEL_FROM]);
|
||||
if (!result->source) {
|
||||
qWarning() << "Invalid route source " << obj[JSON_CHANNEL_FROM];
|
||||
return Route::Pointer();
|
||||
}
|
||||
result->destination = parseEndpoint(obj[JSON_CHANNEL_TO]);
|
||||
|
||||
|
||||
result->destination = parseDestination(obj[JSON_CHANNEL_TO]);
|
||||
if (!result->destination) {
|
||||
qWarning() << "Invalid route destination " << obj[JSON_CHANNEL_TO];
|
||||
return Route::Pointer();
|
||||
}
|
||||
|
||||
if (obj.contains(JSON_CHANNEL_WHEN)) {
|
||||
auto when = parseConditional(obj[JSON_CHANNEL_WHEN]);
|
||||
if (!when) {
|
||||
qWarning() << "Invalid route conditional " << obj[JSON_CHANNEL_TO];
|
||||
auto conditionalsValue = obj[JSON_CHANNEL_WHEN];
|
||||
result->conditional = parseConditional(conditionalsValue);
|
||||
if (!result->conditional) {
|
||||
qWarning() << "Invalid route conditionals " << conditionalsValue;
|
||||
return Route::Pointer();
|
||||
}
|
||||
result->conditional = when;
|
||||
}
|
||||
|
||||
const auto& filtersValue = obj[JSON_CHANNEL_FILTERS];
|
||||
// FIXME support strings for filters with no parameters, both in the array and at the top level...
|
||||
// i.e.
|
||||
// { "from": "Standard.DU", "to" : "Actions.LONGITUDINAL_FORWARD", "filters" : "invert" },
|
||||
// and
|
||||
// { "from": "Standard.DU", "to" : "Actions.LONGITUDINAL_FORWARD", "filters" : [ "invert", "constrainToInteger" ] },
|
||||
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);
|
||||
if (obj.contains(JSON_CHANNEL_FILTERS)) {
|
||||
auto filtersValue = obj[JSON_CHANNEL_FILTERS];
|
||||
result->filters = parseFilters(filtersValue);
|
||||
if (result->filters.empty()) {
|
||||
qWarning() << "Invalid route filters " << filtersValue;
|
||||
return Route::Pointer();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ namespace controller {
|
|||
using MappingStack = std::list<Mapping::Pointer>;
|
||||
using InputToEndpointMap = std::map<Input, 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 EndpointPairMap = std::map<EndpointPair, Endpoint::Pointer>;
|
||||
using DevicesMap = std::map<int, DeviceProxy::Pointer>;
|
||||
|
@ -86,9 +86,9 @@ namespace controller {
|
|||
int findAction(const QString& actionName) const;
|
||||
QVector<QString> getActionNames() const;
|
||||
|
||||
void setActionState(Action action, float value) { _externalActionStates[toInt(action)] = value; }
|
||||
void deltaActionState(Action action, float delta) { _externalActionStates[toInt(action)] += delta; }
|
||||
void setActionState(Action action, const Pose& value) { _externalPoseStates[toInt(action)] = value; }
|
||||
void setActionState(Action action, float value) { _actionStates[toInt(action)] = value; }
|
||||
void deltaActionState(Action action, float delta) { _actionStates[toInt(action)] += delta; }
|
||||
void setActionState(Action action, const Pose& value) { _poseStates[toInt(action)] = value; }
|
||||
|
||||
static Input makeStandardInput(controller::StandardButtonChannel button);
|
||||
static Input makeStandardInput(controller::StandardAxisChannel axis);
|
||||
|
@ -119,7 +119,7 @@ namespace controller {
|
|||
void hardwareChanged();
|
||||
|
||||
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.
|
||||
uint16 getFreeDeviceID() { return _nextFreeDeviceID++; }
|
||||
|
||||
|
@ -128,11 +128,9 @@ namespace controller {
|
|||
uint16 _nextFreeDeviceID = STANDARD_DEVICE + 1;
|
||||
|
||||
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> _lastActionStates = std::vector<float>(toInt(Action::NUM_ACTIONS), 0.0f);
|
||||
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;
|
||||
|
||||
|
@ -148,16 +146,22 @@ namespace controller {
|
|||
Endpoint::Pointer endpointFor(const QScriptValue& endpoint);
|
||||
Endpoint::Pointer endpointFor(const Input& endpoint) const;
|
||||
Endpoint::Pointer compositeEndpointFor(Endpoint::Pointer first, Endpoint::Pointer second);
|
||||
|
||||
Mapping::Pointer parseMapping(const QJsonValue& json);
|
||||
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);
|
||||
Conditional::Pointer parseConditional(const QJsonValue& value);
|
||||
|
||||
static Filter::Pointer parseFilter(const QJsonValue& value);
|
||||
static Filter::List parseFilters(const QJsonValue& value);
|
||||
|
||||
InputToEndpointMap _endpointsByInput;
|
||||
EndpointToInputMap _inputsByEndpoint;
|
||||
EndpointPairMap _compositeEndpoints;
|
||||
|
||||
ValueMap _overrideValues;
|
||||
EndpointOverrideMap _overrides;
|
||||
MappingNameMap _mappingsByName;
|
||||
Mapping::Pointer _defaultMapping{ std::make_shared<Mapping>("Default") };
|
||||
MappingDeviceMap _mappingsByDevice;
|
||||
|
|
Loading…
Reference in a new issue