diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 17d16d1718..29d6595c3a 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -148,6 +148,7 @@ function MyController(hand, triggerAction) { this.state = STATE_OFF; this.pointer = null; // entity-id of line object this.triggerValue = 0; // rolling average of trigger value + this.rawTriggerValue = 0; var _this = this; @@ -244,12 +245,17 @@ function MyController(hand, triggerAction) { this.pointer = null; }; - this.updateSmoothedTrigger = function() { - var triggerValue = Controller.getValue(this.triggerAction); + this.eitherTrigger = function (value) { + _this.rawTriggerValue = value; + }; + + this.updateSmoothedTrigger = function () { + var triggerValue = this.rawTriggerValue; // smooth out trigger value this.triggerValue = (this.triggerValue * TRIGGER_SMOOTH_RATIO) + (triggerValue * (1.0 - TRIGGER_SMOOTH_RATIO)); - } + + }; this.triggerSmoothedSqueezed = function() { return this.triggerValue > TRIGGER_ON_VALUE; @@ -259,8 +265,8 @@ function MyController(hand, triggerAction) { return this.triggerValue < TRIGGER_OFF_VALUE; }; - this.triggerSqueezed = function() { - var triggerValue = Controller.getValue(this.triggerAction); + this.triggerSqueezed = function() { + var triggerValue = this.rawTriggerValue; return triggerValue > TRIGGER_ON_VALUE; }; @@ -861,6 +867,14 @@ function MyController(hand, triggerAction) { var rightController = new MyController(RIGHT_HAND, Controller.Standard.RT); var leftController = new MyController(LEFT_HAND, Controller.Standard.LT); +var MAPPING_NAME = "com.highfidelity.handControllerGrab"; + +var mapping = Controller.newMapping(MAPPING_NAME); +mapping.from([Controller.Standard.RB, Controller.Standard.RT]).to(rightController.eitherTrigger); +mapping.from([Controller.Standard.LB, Controller.Standard.LT]).to(leftController.eitherTrigger); +Controller.enableMapping(MAPPING_NAME); + + function update() { rightController.update(); leftController.update(); @@ -869,6 +883,7 @@ function update() { function cleanup() { rightController.cleanup(); leftController.cleanup(); + Controller.disableMapping(MAPPING_NAME); } Script.scriptEnding.connect(cleanup); diff --git a/examples/controllers/hydra/paddleBall.js b/examples/controllers/hydra/paddleBall.js index 13c6e2eb62..d90a78c260 100644 --- a/examples/controllers/hydra/paddleBall.js +++ b/examples/controllers/hydra/paddleBall.js @@ -20,10 +20,11 @@ var BALL_SIZE = 0.08; var PADDLE_SIZE = 0.20; var PADDLE_THICKNESS = 0.06; var PADDLE_COLOR = { red: 184, green: 134, blue: 11 }; -var BALL_COLOR = { red: 255, green: 0, blue: 0 }; +var BALL_COLOR = { red: 0, green: 255, blue: 0 }; var LINE_COLOR = { red: 255, green: 255, blue: 0 }; var PADDLE_BOX_OFFSET = { x: 0.05, y: 0.0, z: 0.0 }; +//probably we need to fix these initial values (offsets and orientation) var HOLD_POSITION_LEFT_OFFSET = { x: -0.15, y: 0.05, z: -0.05 }; var HOLD_POSITION_RIGHT_OFFSET = { x: -0.15, y: 0.05, z: 0.05 }; var PADDLE_ORIENTATION = Quat.fromPitchYawRollDegrees(0,0,0); @@ -32,18 +33,7 @@ var SPRING_FORCE = 15.0; var lastSoundTime = 0; var gameOn = false; var leftHanded = true; -var controllerID; - -function setControllerID() { - if (leftHanded) { - controllerID = 1; - } else { - controllerID = 3; - } -} - -setControllerID(); Menu.addMenu("PaddleBall"); Menu.addMenuItem({ menuName: "PaddleBall", menuItemName: "Left-Handed", isCheckable: true, isChecked: true }); @@ -63,7 +53,7 @@ var ball, paddle, paddleModel, line; function createEntities() { ball = Entities.addEntity( { type: "Sphere", - position: Controller.getSpatialControlPosition(controllerID), + position: leftHanded ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation, dimensions: { x: BALL_SIZE, y: BALL_SIZE, z: BALL_SIZE }, color: BALL_COLOR, gravity: { x: 0, y: GRAVITY, z: 0 }, @@ -73,28 +63,28 @@ function createEntities() { paddle = Entities.addEntity( { type: "Box", - position: Controller.getSpatialControlPosition(controllerID), + position: leftHanded ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation, dimensions: { x: PADDLE_SIZE, y: PADDLE_THICKNESS, z: PADDLE_SIZE * 0.80 }, color: PADDLE_COLOR, gravity: { x: 0, y: 0, z: 0 }, ignoreCollisions: false, damping: 0.10, visible: false, - rotation: Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(controllerID)), - collisionsWillMove: false }); + rotation : leftHanded ? MyAvatar.leftHandPose.rotation : MyAvatar.rightHandPose.rotation, + collisionsWillMove: false }); modelURL = "http://public.highfidelity.io/models/attachments/pong_paddle.fbx"; paddleModel = Entities.addEntity( { type: "Model", - position: Vec3.sum(Controller.getSpatialControlPosition(controllerID), PADDLE_BOX_OFFSET), + position: Vec3.sum( leftHanded ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation, PADDLE_BOX_OFFSET), dimensions: { x: PADDLE_SIZE * 1.5, y: PADDLE_THICKNESS, z: PADDLE_SIZE * 1.25 }, color: PADDLE_COLOR, gravity: { x: 0, y: 0, z: 0 }, ignoreCollisions: true, modelURL: modelURL, damping: 0.10, - rotation: Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(controllerID)), - collisionsWillMove: false }); + rotation : leftHanded ? MyAvatar.leftHandPose.rotation : MyAvatar.rightHandPose.rotation, + collisionsWillMove: false }); line = Overlays.addOverlay("line3d", { start: { x: 0, y: 0, z: 0 }, @@ -118,7 +108,7 @@ function deleteEntities() { } function update(deltaTime) { - var palmPosition = Controller.getSpatialControlPosition(controllerID); + var palmPosition = leftHanded ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation; var controllerActive = (Vec3.length(palmPosition) > 0); if (!gameOn && controllerActive) { @@ -133,8 +123,8 @@ function update(deltaTime) { } var paddleOrientation = leftHanded ? PADDLE_ORIENTATION : Quat.multiply(PADDLE_ORIENTATION, Quat.fromPitchYawRollDegrees(0, 180, 0)); - var paddleWorldOrientation = Quat.multiply(Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(controllerID)), paddleOrientation); - var holdPosition = Vec3.sum(leftHanded ? MyAvatar.getLeftPalmPosition() : MyAvatar.getRightPalmPosition(), + var paddleWorldOrientation = Quat.multiply(leftHanded ? MyAvatar.leftHandPose.rotation : MyAvatar.rightHandPose.rotation, paddleOrientation); + var holdPosition = Vec3.sum(leftHanded ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation, Vec3.multiplyQbyV(paddleWorldOrientation, leftHanded ? HOLD_POSITION_LEFT_OFFSET : HOLD_POSITION_RIGHT_OFFSET )); var props = Entities.getEntityProperties(ball); @@ -146,10 +136,10 @@ function update(deltaTime) { Entities.editEntity(ball, { velocity: ballVelocity }); Overlays.editOverlay(line, { start: props.position, end: holdPosition }); Entities.editEntity(paddle, { position: holdPosition, - velocity: Controller.getSpatialControlVelocity(controllerID), + velocity: leftHanded ? MyAvatar.leftHandPose.velocity : MyAvatar.rightHandPose.velocity, rotation: paddleWorldOrientation }); Entities.editEntity(paddleModel, { position: Vec3.sum(holdPosition, Vec3.multiplyQbyV(paddleWorldOrientation, PADDLE_BOX_OFFSET)), - velocity: Controller.getSpatialControlVelocity(controllerID), + velocity: leftHanded ? MyAvatar.leftHandPose.velocity : MyAvatar.rightHandPose.velocity, rotation: paddleWorldOrientation }); } @@ -182,7 +172,6 @@ function menuItemEvent(menuItem) { leftHanded = Menu.isOptionChecked("Left-Handed"); } if ((leftHanded != oldHanded) && gameOn) { - setControllerID(); deleteEntities(); createEntities(); } diff --git a/examples/example/games/hydraGrabHockey.js b/examples/example/games/hydraGrabHockey.js index b9f760fa08..26f9a443ab 100644 --- a/examples/example/games/hydraGrabHockey.js +++ b/examples/example/games/hydraGrabHockey.js @@ -59,9 +59,7 @@ function controller(side) { this.triggerHeld = false; this.triggerThreshold = 0.9; this.side = side; - this.palm = 2 * side; - this.tip = 2 * side + 1; - this.trigger = side; + this.trigger = side == LEFT ? Controller.Standard.LT : Controller.Standard.RT; this.originalGravity = { x: 0, y: 0, @@ -150,8 +148,8 @@ function controller(side) { this.updateControllerState = function() { - this.palmPosition = Controller.getSpatialControlPosition(this.palm); - this.tipPosition = Controller.getSpatialControlPosition(this.tip); + this.palmPosition = this.side == RIGHT ? MyAvatar.rightHandPose.translation : MyAvatar.leftHandPose.translation; + this.tipPosition = this.side == RIGHT ? MyAvatar.rightHandTipPose.translation : MyAvatar.leftHandTipPose.translation; this.triggerValue = Controller.getTriggerValue(this.trigger); } diff --git a/examples/example/painting/hydraPaint.js b/examples/example/painting/hydraPaint.js index 29a3323e72..36137945cc 100644 --- a/examples/example/painting/hydraPaint.js +++ b/examples/example/painting/hydraPaint.js @@ -71,10 +71,8 @@ function controller(side, cycleColorButton) { this.triggerHeld = false; this.triggerThreshold = 0.9; this.side = side; - this.palm = 2 * side; - this.tip = 2 * side + 1; - this.trigger = side; - this.cycleColorButton = cycleColorButton; + this.trigger = side == LEFT ? Controller.Stantard.LT : Controller.Standard.RT; + this.cycleColorButton = side == LEFT ? Controller.Stantard.LeftPrimaryThumb : Controller.Standard.RightPrimaryThumb; this.points = []; this.normals = []; @@ -173,11 +171,10 @@ function controller(side, cycleColorButton) { this.updateControllerState = function() { - this.cycleColorButtonPressed = Controller.isButtonPressed(this.cycleColorButton); - this.palmPosition = Controller.getSpatialControlPosition(this.palm); - this.tipPosition = Controller.getSpatialControlPosition(this.tip); - this.palmNormal = Controller.getSpatialControlNormal(this.palm); - this.triggerValue = Controller.getTriggerValue(this.trigger); + this.cycleColorButtonPressed = Controller.getValue(this.cycleColorButton); + this.palmPosition = this.side == RIGHT ? MyAvatar.rightHandPose.translation : MyAvatar.leftHandPose.translation; + this.tipPosition = this.side == RIGHT ? MyAvatar.rightHandTipPose.translation : MyAvatar.leftHandTipPose.translation; + this.triggerValue = Controller.getValue(this.trigger); if (this.prevCycleColorButtonPressed === true && this.cycleColorButtonPressed === false) { @@ -215,8 +212,8 @@ function vectorIsZero(v) { } -var rightController = new controller(RIGHT, RIGHT_BUTTON_4); -var leftController = new controller(LEFT, LEFT_BUTTON_4); +var rightController = new controller(RIGHT); +var leftController = new controller(LEFT); Script.update.connect(update); Script.scriptEnding.connect(scriptEnding); diff --git a/examples/tests/controllerInterfaceTest.js b/examples/tests/controllerInterfaceTest.js index 0dccd1209a..97ad9bbc38 100644 --- a/examples/tests/controllerInterfaceTest.js +++ b/examples/tests/controllerInterfaceTest.js @@ -4,8 +4,10 @@ ControllerTest = function() { var xbox = Controller.Hardware.GamePad; this.mappingEnabled = false; this.mapping = Controller.newMapping(); + this.mapping.from(standard.LX).when([standard.LB, standard.RB]).to(actions.Yaw); this.mapping.from(standard.RX).to(actions.StepYaw); this.mapping.from(standard.RY).invert().to(actions.Pitch); + this.mapping.from(standard.RY).invert().to(actions.Pitch); var testMakeAxis = false; diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index e924718fca..2579c7dbec 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -24,9 +24,22 @@ #include "Logging.h" -#include "Endpoint.h" -#include "Route.h" -#include "Mapping.h" +#include "impl/conditionals/AndConditional.h" +#include "impl/conditionals/EndpointConditional.h" +#include "impl/conditionals/ScriptConditional.h" + +#include "impl/endpoints/ActionEndpoint.h" +#include "impl/endpoints/AnyEndpoint.h" +#include "impl/endpoints/ArrayEndpoint.h" +#include "impl/endpoints/CompositeEndpoint.h" +#include "impl/endpoints/InputEndpoint.h" +#include "impl/endpoints/JSEndpoint.h" +#include "impl/endpoints/ScriptEndpoint.h" +#include "impl/endpoints/StandardEndpoint.h" + +#include "impl/Route.h" +#include "impl/Mapping.h" + namespace controller { const uint16_t UserInputMapper::ACTIONS_DEVICE = Input::INVALID_DEVICE - 0xFF; @@ -42,300 +55,6 @@ controller::UserInputMapper::UserInputMapper() { namespace controller { -class ScriptEndpoint : public Endpoint { - Q_OBJECT; -public: - ScriptEndpoint(const QScriptValue& callable) - : Endpoint(Input::INVALID_INPUT), _callable(callable) { - } - - virtual float value(); - virtual void apply(float newValue, float oldValue, const Pointer& source); - -protected: - Q_INVOKABLE void updateValue(); - Q_INVOKABLE virtual void internalApply(float newValue, float oldValue, int sourceID); -private: - QScriptValue _callable; - float _lastValue = 0.0f; -}; - -class StandardEndpoint : public VirtualEndpoint { -public: - 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 { - _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 void apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) override { - if (newValue != Pose()) { - _written = true; - } - VirtualEndpoint::apply(newValue, oldValue, source); - } - -private: - bool _written { false }; - bool _read { false }; -}; - - -class JSEndpoint : public Endpoint { -public: - JSEndpoint(const QJSValue& callable) - : Endpoint(Input::INVALID_INPUT), _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; -}; - -float ScriptEndpoint::value() { - updateValue(); - return _lastValue; -} - -void ScriptEndpoint::updateValue() { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "updateValue", Qt::QueuedConnection); - return; - } - - _lastValue = (float)_callable.call().toNumber(); -} - -void ScriptEndpoint::apply(float newValue, float oldValue, const Pointer& source) { - internalApply(newValue, oldValue, source->getInput().getID()); -} - -void ScriptEndpoint::internalApply(float newValue, float oldValue, int sourceID) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "internalApply", Qt::QueuedConnection, - Q_ARG(float, newValue), - Q_ARG(float, oldValue), - Q_ARG(int, sourceID)); - return; - } - _callable.call(QScriptValue(), - QScriptValueList({ QScriptValue(newValue), QScriptValue(oldValue), QScriptValue(sourceID) })); -} - -static const Input INVALID_STANDARD_INPUT = Input(UserInputMapper::STANDARD_DEVICE, Input::INVALID_CHANNEL, ChannelType::INVALID); - -class CompositeEndpoint : public Endpoint, Endpoint::Pair { -public: - CompositeEndpoint(Endpoint::Pointer first, Endpoint::Pointer second) - : Endpoint(Input::INVALID_INPUT), Pair(first, second) { - if (first->getInput().device == UserInputMapper::STANDARD_DEVICE && - second->getInput().device == UserInputMapper::STANDARD_DEVICE) { - this->_input = INVALID_STANDARD_INPUT; - } - } - - virtual float value() { - float result = first->value() * -1.0f + second->value(); - return result; - } - - virtual void apply(float newValue, float oldValue, const Pointer& source) { - // Composites are read only - } -}; - -class ArrayEndpoint : public Endpoint { - friend class UserInputMapper; -public: - using Pointer = std::shared_ptr; - 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(Endpoint::List children) : Endpoint(Input::INVALID_INPUT), _children(children) { - bool standard = true; - // Ensure if we're building a composite of standard devices the composite itself - // is treated as a standard device for rule processing order - for (auto endpoint : children) { - if (endpoint->getInput().device != UserInputMapper::STANDARD_DEVICE) { - standard = false; - break; - } - } - if (standard) { - this->_input = INVALID_STANDARD_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"); - } - - // AnyEndpoint is used for reading, so return false if any child returns false (has been written to) - virtual bool writeable() const override { - for (auto& child : _children) { - if (!child->writeable()) { - return false; - } - } - return true; - } - - 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: - InputEndpoint(const Input& id = Input::INVALID_INPUT) - : Endpoint(id) { - } - - virtual float value() override { - _read = true; - if (isPose()) { - return pose().valid ? 1.0f : 0.0f; - } - auto userInputMapper = DependencyManager::get(); - auto deviceProxy = userInputMapper->getDeviceProxy(_input); - if (!deviceProxy) { - return 0.0f; - } - 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 { - _read = true; - if (!isPose()) { - return Pose(); - } - auto userInputMapper = DependencyManager::get(); - auto deviceProxy = userInputMapper->getDeviceProxy(_input); - if (!deviceProxy) { - return Pose(); - } - return deviceProxy->getPose(_input, 0); - } - - virtual void apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) override { } - - virtual bool writeable() const { return false; } - virtual bool readable() const { return !_read; } - virtual void reset() { _read = false; } - -private: - bool _read { false }; -}; - -class ActionEndpoint : public Endpoint { -public: - ActionEndpoint(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; - if (_input != Input::INVALID_INPUT) { - auto userInputMapper = DependencyManager::get(); - userInputMapper->deltaActionState(Action(_input.getChannel()), newValue); - } - } - - virtual Pose pose() override { return _currentPose; } - virtual void apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) override { - _currentPose = newValue; - if (!_currentPose.isValid()) { - return; - } - if (_input != Input::INVALID_INPUT) { - auto userInputMapper = DependencyManager::get(); - userInputMapper->setActionState(Action(_input.getChannel()), _currentPose); - } - } - - virtual void reset() override { - _currentValue = 0.0f; - _currentPose = Pose(); - } - -private: - float _currentValue{ 0.0f }; - Pose _currentPose{}; -}; - UserInputMapper::~UserInputMapper() { } @@ -856,6 +575,21 @@ Endpoint::Pointer UserInputMapper::endpointFor(const QScriptValue& endpoint) { return result; } + if (endpoint.isArray()) { + int length = endpoint.property("length").toInteger(); + Endpoint::List children; + for (int i = 0; i < length; i++) { + QScriptValue arrayItem = endpoint.property(i); + Endpoint::Pointer destination = endpointFor(arrayItem); + if (!destination) { + return Endpoint::Pointer(); + } + children.push_back(destination); + } + return std::make_shared(children); + } + + qWarning() << "Unsupported input type " << endpoint.toString(); return Endpoint::Pointer(); } @@ -1004,32 +738,46 @@ Endpoint::Pointer UserInputMapper::parseEndpoint(const QJsonValue& value) { return result; } -class AndConditional : public Conditional { -public: - using Pointer = std::shared_ptr; - AndConditional(Conditional::List children) : _children(children) { } +Conditional::Pointer UserInputMapper::conditionalFor(const QJSValue& condition) { + return Conditional::Pointer(); +} - virtual bool satisfied() override { - for (auto& conditional : _children) { - if (!conditional->satisfied()) { - return false; +Conditional::Pointer UserInputMapper::conditionalFor(const QScriptValue& condition) { + if (condition.isArray()) { + int length = condition.property("length").toInteger(); + Conditional::List children; + for (int i = 0; i < length; i++) { + Conditional::Pointer destination = conditionalFor(condition.property(i)); + if (!destination) { + return Conditional::Pointer(); } + children.push_back(destination); } - return true; + return std::make_shared(children); } -private: - Conditional::List _children; -}; + if (condition.isNumber()) { + return conditionalFor(Input(condition.toInt32())); + } -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; -}; + if (condition.isFunction()) { + return std::make_shared(condition); + } + + qWarning() << "Unsupported conditional type " << condition.toString(); + return Conditional::Pointer(); +} + +Conditional::Pointer UserInputMapper::conditionalFor(const Input& inputId) const { + Locker locker(_lock); + auto iterator = _endpointsByInput.find(inputId); + if (_endpointsByInput.end() == iterator) { + qWarning() << "Unknown input: " << QString::number(inputId.getID(), 16); + return Conditional::Pointer(); + } + return std::make_shared(iterator->second); +} Conditional::Pointer UserInputMapper::parseConditional(const QJsonValue& value) { if (value.isArray()) { @@ -1057,7 +805,6 @@ Conditional::Pointer UserInputMapper::parseConditional(const QJsonValue& value) return Conditional::parse(value); } - Filter::Pointer UserInputMapper::parseFilter(const QJsonValue& value) { Filter::Pointer result; if (value.isString()) { @@ -1073,7 +820,6 @@ Filter::Pointer UserInputMapper::parseFilter(const QJsonValue& value) { return result; } - Filter::List UserInputMapper::parseFilters(const QJsonValue& value) { if (value.isNull()) { return Filter::List(); @@ -1275,4 +1021,3 @@ void UserInputMapper::disableMapping(const Mapping::Pointer& mapping) { } -#include "UserInputMapper.moc" diff --git a/libraries/controllers/src/controllers/UserInputMapper.h b/libraries/controllers/src/controllers/UserInputMapper.h index 319037fcb1..0a6ed3acad 100644 --- a/libraries/controllers/src/controllers/UserInputMapper.h +++ b/libraries/controllers/src/controllers/UserInputMapper.h @@ -143,6 +143,7 @@ namespace controller { friend class MappingBuilderProxy; void runMappings(); + static void applyRoutes(const RouteList& route); static bool applyRoute(const RoutePointer& route, bool force = false); void enableMapping(const MappingPointer& mapping); @@ -151,6 +152,9 @@ namespace controller { EndpointPointer endpointFor(const QScriptValue& endpoint); EndpointPointer endpointFor(const Input& endpoint) const; EndpointPointer compositeEndpointFor(EndpointPointer first, EndpointPointer second); + ConditionalPointer conditionalFor(const QJSValue& endpoint); + ConditionalPointer conditionalFor(const QScriptValue& endpoint); + ConditionalPointer conditionalFor(const Input& endpoint) const; MappingPointer parseMapping(const QJsonValue& json); RoutePointer parseRoute(const QJsonValue& value); diff --git a/libraries/controllers/src/controllers/Conditional.cpp b/libraries/controllers/src/controllers/impl/Conditional.cpp similarity index 100% rename from libraries/controllers/src/controllers/Conditional.cpp rename to libraries/controllers/src/controllers/impl/Conditional.cpp diff --git a/libraries/controllers/src/controllers/Conditional.h b/libraries/controllers/src/controllers/impl/Conditional.h similarity index 96% rename from libraries/controllers/src/controllers/Conditional.h rename to libraries/controllers/src/controllers/impl/Conditional.h index 4d67d2871e..a216c8789f 100644 --- a/libraries/controllers/src/controllers/Conditional.h +++ b/libraries/controllers/src/controllers/impl/Conditional.h @@ -28,6 +28,7 @@ namespace controller { using Pointer = std::shared_ptr; using List = std::list; using Factory = hifi::SimpleFactory; + using Lambda = std::function; virtual bool satisfied() = 0; virtual bool parseParameters(const QJsonValue& parameters) { return true; } diff --git a/libraries/controllers/src/controllers/Endpoint.cpp b/libraries/controllers/src/controllers/impl/Endpoint.cpp similarity index 100% rename from libraries/controllers/src/controllers/Endpoint.cpp rename to libraries/controllers/src/controllers/impl/Endpoint.cpp diff --git a/libraries/controllers/src/controllers/Endpoint.h b/libraries/controllers/src/controllers/impl/Endpoint.h similarity index 98% rename from libraries/controllers/src/controllers/Endpoint.h rename to libraries/controllers/src/controllers/impl/Endpoint.h index 7a94b06e7e..f5fe058d82 100644 --- a/libraries/controllers/src/controllers/Endpoint.h +++ b/libraries/controllers/src/controllers/impl/Endpoint.h @@ -16,8 +16,8 @@ #include -#include "Input.h" -#include "Pose.h" +#include "../Input.h" +#include "../Pose.h" class QScriptValue; diff --git a/libraries/controllers/src/controllers/Filter.cpp b/libraries/controllers/src/controllers/impl/Filter.cpp similarity index 100% rename from libraries/controllers/src/controllers/Filter.cpp rename to libraries/controllers/src/controllers/impl/Filter.cpp diff --git a/libraries/controllers/src/controllers/Filter.h b/libraries/controllers/src/controllers/impl/Filter.h similarity index 100% rename from libraries/controllers/src/controllers/Filter.h rename to libraries/controllers/src/controllers/impl/Filter.h diff --git a/libraries/controllers/src/controllers/Mapping.cpp b/libraries/controllers/src/controllers/impl/Mapping.cpp similarity index 100% rename from libraries/controllers/src/controllers/Mapping.cpp rename to libraries/controllers/src/controllers/impl/Mapping.cpp diff --git a/libraries/controllers/src/controllers/Mapping.h b/libraries/controllers/src/controllers/impl/Mapping.h similarity index 100% rename from libraries/controllers/src/controllers/Mapping.h rename to libraries/controllers/src/controllers/impl/Mapping.h diff --git a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h index 93aa022647..ac9a5a300d 100644 --- a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h +++ b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h @@ -12,8 +12,8 @@ #include #include -#include "../Mapping.h" -#include "../Endpoint.h" +#include "Mapping.h" +#include "Endpoint.h" class QJSValue; class QScriptValue; diff --git a/libraries/controllers/src/controllers/Route.cpp b/libraries/controllers/src/controllers/impl/Route.cpp similarity index 100% rename from libraries/controllers/src/controllers/Route.cpp rename to libraries/controllers/src/controllers/impl/Route.cpp diff --git a/libraries/controllers/src/controllers/Route.h b/libraries/controllers/src/controllers/impl/Route.h similarity index 100% rename from libraries/controllers/src/controllers/Route.h rename to libraries/controllers/src/controllers/impl/Route.h diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp index c0d0758e4e..d56d699c28 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp @@ -33,7 +33,6 @@ void RouteBuilderProxy::to(const QScriptValue& destination) { } void RouteBuilderProxy::to(const Endpoint::Pointer& destination) { - auto sourceEndpoint = _route->source; _route->destination = destination; _mapping->routes.push_back(_route); deleteLater(); @@ -56,6 +55,17 @@ QObject* RouteBuilderProxy::filterQml(const QJSValue& expression) { return this; } +QObject* RouteBuilderProxy::when(const QScriptValue& expression) { + _route->conditional = _parent.conditionalFor(expression); + return this; +} + +QObject* RouteBuilderProxy::whenQml(const QJSValue& expression) { + _route->conditional = _parent.conditionalFor(expression); + return this; +} + + QObject* RouteBuilderProxy::filter(const QScriptValue& expression) { return this; } diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h index 0484c5890d..4bcfba5acd 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h @@ -11,10 +11,12 @@ #include -#include "../Filter.h" -#include "../Route.h" -#include "../Mapping.h" +#include "Filter.h" +#include "Route.h" +#include "Mapping.h" + #include "../UserInputMapper.h" + class QJSValue; class QScriptValue; class QJsonValue; @@ -33,9 +35,11 @@ class RouteBuilderProxy : public QObject { Q_INVOKABLE void toQml(const QJSValue& destination); Q_INVOKABLE QObject* filterQml(const QJSValue& expression); + Q_INVOKABLE QObject* whenQml(const QJSValue& expression); Q_INVOKABLE void to(const QScriptValue& destination); Q_INVOKABLE QObject* debug(bool enable = true); + Q_INVOKABLE QObject* when(const QScriptValue& expression); Q_INVOKABLE QObject* filter(const QScriptValue& expression); Q_INVOKABLE QObject* clamp(float min, float max); Q_INVOKABLE QObject* pulse(float interval); @@ -47,6 +51,7 @@ class RouteBuilderProxy : public QObject { private: void to(const Endpoint::Pointer& destination); + void conditional(const Conditional::Pointer& conditional); void addFilter(Filter::Lambda lambda); void addFilter(Filter::Pointer filter); UserInputMapper& _parent; diff --git a/libraries/controllers/src/controllers/impl/conditionals/AndConditional.cpp b/libraries/controllers/src/controllers/impl/conditionals/AndConditional.cpp new file mode 100644 index 0000000000..772d4f1314 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/conditionals/AndConditional.cpp @@ -0,0 +1,21 @@ +// +// Created by Bradley Austin Davis 2015/10/23 +// 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 "AndConditional.h" + +using namespace controller; + +bool AndConditional::satisfied() { + for (auto& conditional : _children) { + if (!conditional->satisfied()) { + return false; + } + } + return true; +} + diff --git a/libraries/controllers/src/controllers/impl/conditionals/AndConditional.h b/libraries/controllers/src/controllers/impl/conditionals/AndConditional.h new file mode 100644 index 0000000000..c60e4b15df --- /dev/null +++ b/libraries/controllers/src/controllers/impl/conditionals/AndConditional.h @@ -0,0 +1,31 @@ +// +// Created by Bradley Austin Davis 2015/10/20 +// 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 +#ifndef hifi_Controllers_AndConditional_h +#define hifi_Controllers_AndConditional_h + +#include "../Conditional.h" + +namespace controller { + +class AndConditional : public Conditional { +public: + using Pointer = std::shared_ptr; + + AndConditional(Conditional::List children) : _children(children) { } + + virtual bool satisfied() override; + +private: + Conditional::List _children; +}; + +} + +#endif diff --git a/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.cpp b/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.cpp new file mode 100644 index 0000000000..03e16b8cf9 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.cpp @@ -0,0 +1,9 @@ +// +// Created by Bradley Austin Davis 2015/10/23 +// 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 "EndpointConditional.h" \ No newline at end of file diff --git a/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.h b/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.h new file mode 100644 index 0000000000..1e4205afc7 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.h @@ -0,0 +1,27 @@ +// +// Created by Bradley Austin Davis 2015/10/20 +// 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 +#ifndef hifi_Controllers_EndpointConditional_h +#define hifi_Controllers_EndpointConditional_h + +#include "../Conditional.h" +#include "../Endpoint.h" + +namespace controller { + +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; +}; + +} +#endif diff --git a/libraries/controllers/src/controllers/impl/conditionals/NotConditional.cpp b/libraries/controllers/src/controllers/impl/conditionals/NotConditional.cpp new file mode 100644 index 0000000000..0c8d602b9e --- /dev/null +++ b/libraries/controllers/src/controllers/impl/conditionals/NotConditional.cpp @@ -0,0 +1,9 @@ +// +// Created by Bradley Austin Davis 2015/10/23 +// 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 "NotConditional.h" diff --git a/libraries/controllers/src/controllers/impl/conditionals/NotConditional.h b/libraries/controllers/src/controllers/impl/conditionals/NotConditional.h new file mode 100644 index 0000000000..3acda07106 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/conditionals/NotConditional.h @@ -0,0 +1,16 @@ +// +// Created by Bradley Austin Davis 2015/10/20 +// 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 +#ifndef hifi_Controllers_NotConditional_h +#define hifi_Controllers_NotConditional_h + +#include "../Conditional.h" + + +#endif diff --git a/libraries/controllers/src/controllers/impl/conditionals/ScriptConditional.cpp b/libraries/controllers/src/controllers/impl/conditionals/ScriptConditional.cpp new file mode 100644 index 0000000000..277b63ed11 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/conditionals/ScriptConditional.cpp @@ -0,0 +1,27 @@ +// +// Created by Bradley Austin Davis 2015/10/23 +// 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 "ScriptConditional.h" + +#include + +using namespace controller; + +bool ScriptConditional::satisfied() { + updateValue(); + return _lastValue; +} + +void ScriptConditional::updateValue() { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "updateValue", Qt::QueuedConnection); + return; + } + + _lastValue = _callable.call().toBool(); +} diff --git a/libraries/controllers/src/controllers/impl/conditionals/ScriptConditional.h b/libraries/controllers/src/controllers/impl/conditionals/ScriptConditional.h new file mode 100644 index 0000000000..800692d02c --- /dev/null +++ b/libraries/controllers/src/controllers/impl/conditionals/ScriptConditional.h @@ -0,0 +1,34 @@ +// +// Created by Bradley Austin Davis 2015/10/20 +// 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 +#ifndef hifi_Controllers_ScriptConditional_h +#define hifi_Controllers_ScriptConditional_h + +#include + +#include + +#include "../Conditional.h" + +namespace controller { + +class ScriptConditional : public QObject, public Conditional { + Q_OBJECT; +public: + ScriptConditional(const QScriptValue& callable) : _callable(callable) { } + virtual bool satisfied() override; +protected: + Q_INVOKABLE void updateValue(); +private: + QScriptValue _callable; + bool _lastValue { false }; +}; + +} +#endif diff --git a/libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.cpp b/libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.cpp new file mode 100644 index 0000000000..d07ef38185 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.cpp @@ -0,0 +1,40 @@ +// +// Created by Bradley Austin Davis 2015/10/23 +// 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 "ActionEndpoint.h" + +#include + +#include "../../UserInputMapper.h" + +using namespace controller; + +void ActionEndpoint::apply(float newValue, float oldValue, const Pointer& source) { + _currentValue += newValue; + if (_input != Input::INVALID_INPUT) { + auto userInputMapper = DependencyManager::get(); + userInputMapper->deltaActionState(Action(_input.getChannel()), newValue); + } +} + +void ActionEndpoint::apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) { + _currentPose = newValue; + if (!_currentPose.isValid()) { + return; + } + if (_input != Input::INVALID_INPUT) { + auto userInputMapper = DependencyManager::get(); + userInputMapper->setActionState(Action(_input.getChannel()), _currentPose); + } +} + +void ActionEndpoint::reset() { + _currentValue = 0.0f; + _currentPose = Pose(); +} + diff --git a/libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.h new file mode 100644 index 0000000000..eaae1e3798 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.h @@ -0,0 +1,41 @@ +// +// Created by Bradley Austin Davis 2015/10/23 +// 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 +#ifndef hifi_Controllers_ActionEndpoint_h +#define hifi_Controllers_ActionEndpoint_h + +#include "../Endpoint.h" + +#include "../../Actions.h" +#include + +#include "../../UserInputMapper.h" + +namespace controller { + +class ActionEndpoint : public Endpoint { +public: + ActionEndpoint(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; + + virtual Pose pose() override { return _currentPose; } + virtual void apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) override; + + virtual void reset() override; + +private: + float _currentValue{ 0.0f }; + Pose _currentPose{}; +}; + +} + +#endif diff --git a/libraries/controllers/src/controllers/impl/endpoints/AnyEndpoint.cpp b/libraries/controllers/src/controllers/impl/endpoints/AnyEndpoint.cpp new file mode 100644 index 0000000000..b3310e4424 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/endpoints/AnyEndpoint.cpp @@ -0,0 +1,63 @@ +// +// Created by Bradley Austin Davis 2015/10/23 +// 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 "AnyEndpoint.h" + +#include "../../UserInputMapper.h" + +using namespace controller; + +AnyEndpoint::AnyEndpoint(Endpoint::List children) : Endpoint(Input::INVALID_INPUT), _children(children) { + bool standard = true; + // Ensure if we're building a composite of standard devices the composite itself + // is treated as a standard device for rule processing order + for (auto endpoint : children) { + if (endpoint->getInput().device != UserInputMapper::STANDARD_DEVICE) { + standard = false; + break; + } + } + if (standard) { + this->_input.device = UserInputMapper::STANDARD_DEVICE; + } +} + +float AnyEndpoint::value() { + float result = 0; + for (auto& child : _children) { + float childResult = child->value(); + if (childResult != 0.0f) { + result = childResult; + } + } + return result; +} + +void AnyEndpoint::apply(float newValue, float oldValue, const Endpoint::Pointer& source) { + qFatal("AnyEndpoint is read only"); +} + +// AnyEndpoint is used for reading, so return false if any child returns false (has been written to) +bool AnyEndpoint::writeable() const { + for (auto& child : _children) { + if (!child->writeable()) { + return false; + } + } + return true; +} + +bool AnyEndpoint::readable() const { + for (auto& child : _children) { + if (!child->readable()) { + return false; + } + } + return true; +} + diff --git a/libraries/controllers/src/controllers/impl/endpoints/AnyEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/AnyEndpoint.h new file mode 100644 index 0000000000..25db6a5a2a --- /dev/null +++ b/libraries/controllers/src/controllers/impl/endpoints/AnyEndpoint.h @@ -0,0 +1,32 @@ +// +// Created by Bradley Austin Davis 2015/10/23 +// 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 +#ifndef hifi_Controllers_AnyEndpoint_h +#define hifi_Controllers_AnyEndpoint_h + +#include "../Endpoint.h" + +namespace controller { + +class AnyEndpoint : public Endpoint { + friend class UserInputMapper; +public: + AnyEndpoint(Endpoint::List children); + virtual float value() override; + virtual void apply(float newValue, float oldValue, const Endpoint::Pointer& source) override; + virtual bool writeable() const override; + virtual bool readable() const override; + +private: + Endpoint::List _children; +}; + +} + +#endif diff --git a/libraries/controllers/src/controllers/impl/endpoints/ArrayEndpoint.cpp b/libraries/controllers/src/controllers/impl/endpoints/ArrayEndpoint.cpp new file mode 100644 index 0000000000..c083a7147d --- /dev/null +++ b/libraries/controllers/src/controllers/impl/endpoints/ArrayEndpoint.cpp @@ -0,0 +1,9 @@ +// +// Created by Bradley Austin Davis 2015/10/23 +// 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 "ArrayEndpoint.h" \ No newline at end of file diff --git a/libraries/controllers/src/controllers/impl/endpoints/ArrayEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/ArrayEndpoint.h new file mode 100644 index 0000000000..676f43868a --- /dev/null +++ b/libraries/controllers/src/controllers/impl/endpoints/ArrayEndpoint.h @@ -0,0 +1,43 @@ +// +// Created by Bradley Austin Davis 2015/10/23 +// 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 +#ifndef hifi_Controllers_ArrayEndpoint_h +#define hifi_Controllers_ArrayEndpoint_h + +#include "../Endpoint.h" + +namespace controller { + +class ArrayEndpoint : public Endpoint { + friend class UserInputMapper; +public: + using Pointer = std::shared_ptr; + 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; +}; + +} + +#endif diff --git a/libraries/controllers/src/controllers/impl/endpoints/CompositeEndpoint.cpp b/libraries/controllers/src/controllers/impl/endpoints/CompositeEndpoint.cpp new file mode 100644 index 0000000000..9d89a7d2b3 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/endpoints/CompositeEndpoint.cpp @@ -0,0 +1,32 @@ +// +// Created by Bradley Austin Davis 2015/10/23 +// 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 "CompositeEndpoint.h" + +#include "../../UserInputMapper.h" + +namespace controller { + +CompositeEndpoint::CompositeEndpoint(Endpoint::Pointer first, Endpoint::Pointer second) + : Endpoint(Input::INVALID_INPUT), Pair(first, second) { + if (first->getInput().device == UserInputMapper::STANDARD_DEVICE && + second->getInput().device == UserInputMapper::STANDARD_DEVICE) { + this->_input.device = UserInputMapper::STANDARD_DEVICE; + } +} + +float CompositeEndpoint::value() { + float result = first->value() * -1.0f + second->value(); + return result; +} + +void CompositeEndpoint::apply(float newValue, float oldValue, const Pointer& source) { + // Composites are read only +} + +} diff --git a/libraries/controllers/src/controllers/impl/endpoints/CompositeEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/CompositeEndpoint.h new file mode 100644 index 0000000000..b525a2e4ab --- /dev/null +++ b/libraries/controllers/src/controllers/impl/endpoints/CompositeEndpoint.h @@ -0,0 +1,26 @@ +// +// Created by Bradley Austin Davis 2015/10/23 +// 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 +#ifndef hifi_Controllers_CompositeEndpoint_h +#define hifi_Controllers_CompositeEndpoint_h + +#include "../Endpoint.h" + +namespace controller { + class CompositeEndpoint : public Endpoint, Endpoint::Pair { + public: + CompositeEndpoint(Endpoint::Pointer first, Endpoint::Pointer second); + + virtual float value() override; + virtual void apply(float newValue, float oldValue, const Pointer& source) override; + }; + +} + +#endif diff --git a/libraries/controllers/src/controllers/impl/endpoints/InputEndpoint.cpp b/libraries/controllers/src/controllers/impl/endpoints/InputEndpoint.cpp new file mode 100644 index 0000000000..32cd5b65e0 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/endpoints/InputEndpoint.cpp @@ -0,0 +1,41 @@ +// +// Created by Bradley Austin Davis 2015/10/23 +// 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 "InputEndpoint.h" + +#include + +#include "../../UserInputMapper.h" + +using namespace controller; +float InputEndpoint::value(){ + _read = true; + if (isPose()) { + return pose().valid ? 1.0f : 0.0f; + } + auto userInputMapper = DependencyManager::get(); + auto deviceProxy = userInputMapper->getDeviceProxy(_input); + if (!deviceProxy) { + return 0.0f; + } + return deviceProxy->getValue(_input, 0); +} + +Pose InputEndpoint::pose() { + _read = true; + if (!isPose()) { + return Pose(); + } + auto userInputMapper = DependencyManager::get(); + auto deviceProxy = userInputMapper->getDeviceProxy(_input); + if (!deviceProxy) { + return Pose(); + } + return deviceProxy->getPose(_input, 0); +} + diff --git a/libraries/controllers/src/controllers/impl/endpoints/InputEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/InputEndpoint.h new file mode 100644 index 0000000000..195cd33683 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/endpoints/InputEndpoint.h @@ -0,0 +1,39 @@ +// +// Created by Bradley Austin Davis 2015/10/23 +// 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 +#ifndef hifi_Controllers_InputEndpoint_h +#define hifi_Controllers_InputEndpoint_h + +#include "../Endpoint.h" + +namespace controller { + +class InputEndpoint : public Endpoint { +public: + InputEndpoint(const Input& id = Input::INVALID_INPUT) + : Endpoint(id) { + } + + virtual float value() override; + // 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; + virtual void apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) override { } + + virtual bool writeable() const { return false; } + virtual bool readable() const { return !_read; } + virtual void reset() { _read = false; } + +private: + bool _read { false }; +}; + +} + +#endif diff --git a/libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.cpp b/libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.cpp new file mode 100644 index 0000000000..4560741d12 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.cpp @@ -0,0 +1,9 @@ +// +// Created by Bradley Austin Davis 2015/10/23 +// 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 "JSEndpoint.h" \ No newline at end of file diff --git a/libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.h new file mode 100644 index 0000000000..38ac92bfb6 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.h @@ -0,0 +1,41 @@ +// +// Created by Bradley Austin Davis 2015/10/23 +// 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 +#ifndef hifi_Controllers_JSEndpoint_h +#define hifi_Controllers_JSEndpoint_h + +#include "../Endpoint.h" + +#include +#include + +namespace controller { + +class JSEndpoint : public Endpoint { +public: + JSEndpoint(const QJSValue& callable) + : Endpoint(Input::INVALID_INPUT), _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; +}; + +} + +#endif diff --git a/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.cpp b/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.cpp new file mode 100644 index 0000000000..3dedcef4e4 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.cpp @@ -0,0 +1,43 @@ +// +// Created by Bradley Austin Davis 2015/10/23 +// 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 "ScriptEndpoint.h" + +#include + +using namespace controller; + +float ScriptEndpoint::value() { + updateValue(); + return _lastValue; +} + +void ScriptEndpoint::updateValue() { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "updateValue", Qt::QueuedConnection); + return; + } + + _lastValue = (float)_callable.call().toNumber(); +} + +void ScriptEndpoint::apply(float newValue, float oldValue, const Pointer& source) { + internalApply(newValue, oldValue, source->getInput().getID()); +} + +void ScriptEndpoint::internalApply(float newValue, float oldValue, int sourceID) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "internalApply", Qt::QueuedConnection, + Q_ARG(float, newValue), + Q_ARG(float, oldValue), + Q_ARG(int, sourceID)); + return; + } + _callable.call(QScriptValue(), + QScriptValueList({ QScriptValue(newValue), QScriptValue(oldValue), QScriptValue(sourceID) })); +} diff --git a/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.h new file mode 100644 index 0000000000..e3c7abe812 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.h @@ -0,0 +1,39 @@ +// +// Created by Bradley Austin Davis 2015/10/23 +// 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 +#ifndef hifi_Controllers_ScriptEndpoint_h +#define hifi_Controllers_ScriptEndpoint_h + +#include + +#include "../Endpoint.h" + +namespace controller { + +class ScriptEndpoint : public Endpoint { + Q_OBJECT; +public: + ScriptEndpoint(const QScriptValue& callable) + : Endpoint(Input::INVALID_INPUT), _callable(callable) { + } + + virtual float value(); + virtual void apply(float newValue, float oldValue, const Pointer& source); + +protected: + Q_INVOKABLE void updateValue(); + Q_INVOKABLE virtual void internalApply(float newValue, float oldValue, int sourceID); +private: + QScriptValue _callable; + float _lastValue = 0.0f; +}; + +} + +#endif diff --git a/libraries/controllers/src/controllers/impl/endpoints/StandardEndpoint.cpp b/libraries/controllers/src/controllers/impl/endpoints/StandardEndpoint.cpp new file mode 100644 index 0000000000..09920d249c --- /dev/null +++ b/libraries/controllers/src/controllers/impl/endpoints/StandardEndpoint.cpp @@ -0,0 +1,9 @@ +// +// Created by Bradley Austin Davis 2015/10/23 +// 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 "StandardEndpoint.h" \ No newline at end of file diff --git a/libraries/controllers/src/controllers/impl/endpoints/StandardEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/StandardEndpoint.h new file mode 100644 index 0000000000..44803a22fd --- /dev/null +++ b/libraries/controllers/src/controllers/impl/endpoints/StandardEndpoint.h @@ -0,0 +1,60 @@ +// +// Created by Bradley Austin Davis 2015/10/23 +// 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 +#ifndef hifi_Controllers_StandardEndpoint_h +#define hifi_Controllers_StandardEndpoint_h + +#include "../Endpoint.h" + +namespace controller { + +class StandardEndpoint : public VirtualEndpoint { +public: + 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 { + _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 void apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) override { + if (newValue != Pose()) { + _written = true; + } + VirtualEndpoint::apply(newValue, oldValue, source); + } + +private: + bool _written { false }; + bool _read { false }; +}; + +} + +#endif