Merge pull request #10196 from ZappoMan/poseFilters

support for input controller pose filters
This commit is contained in:
Seth Alves 2017-05-02 09:46:22 -07:00 committed by GitHub
commit b82a333a5e
19 changed files with 361 additions and 1 deletions

View file

@ -69,5 +69,23 @@ namespace controller {
pose.valid = valid;
return pose;
}
Pose Pose::postTransform(const glm::mat4& mat) const {
glm::mat4 original = ::createMatFromQuatAndPos(rotation, translation);
glm::mat4 result = original * mat;
auto translationOut = ::extractTranslation(result);
auto rotationOut = ::glmExtractRotation(result);
auto velocityOut = velocity + glm::cross(angularVelocity, translationOut - translation); // warning: this may be completely wrong
auto angularVelocityOut = angularVelocity;
Pose pose(translationOut,
rotationOut,
velocityOut,
angularVelocityOut);
pose.valid = valid;
return pose;
}
}

View file

@ -41,6 +41,7 @@ namespace controller {
vec3 getAngularVelocity() const { return angularVelocity; }
Pose transform(const glm::mat4& mat) const;
Pose postTransform(const glm::mat4& mat) const;
static QScriptValue toScriptValue(QScriptEngine* engine, const Pose& event);
static void fromScriptValue(const QScriptValue& object, Pose& event);

View file

@ -20,6 +20,8 @@
#include <PathUtils.h>
#include <NumericalConstants.h>
#include <StreamUtils.h>
#include "StandardController.h"
#include "StateController.h"
#include "InputRecorder.h"
@ -563,7 +565,18 @@ bool UserInputMapper::applyRoute(const Route::Pointer& route, bool force) {
if (source->isPose()) {
Pose value = getPose(source, route->peek);
static const Pose IDENTITY_POSE { vec3(), quat() };
if (debugRoutes && route->debug) {
qCDebug(controllers) << "Value was t:" << value.translation << "r:" << value.rotation;
}
// Apply each of the filters.
for (const auto& filter : route->filters) {
value = filter->apply(value);
}
if (debugRoutes && route->debug) {
qCDebug(controllers) << "Filtered value was t:" << value.translation << "r:" << value.rotation;
if (!value.valid) {
qCDebug(controllers) << "Applying invalid pose";
} else if (value == IDENTITY_POSE) {

View file

@ -24,6 +24,10 @@
#include "filters/InvertFilter.h"
#include "filters/PulseFilter.h"
#include "filters/ScaleFilter.h"
#include "filters/TranslateFilter.h"
#include "filters/TransformFilter.h"
#include "filters/PostTransformFilter.h"
#include "filters/RotateFilter.h"
using namespace controller;
@ -37,6 +41,10 @@ REGISTER_FILTER_CLASS_INSTANCE(HysteresisFilter, "hysteresis")
REGISTER_FILTER_CLASS_INSTANCE(InvertFilter, "invert")
REGISTER_FILTER_CLASS_INSTANCE(ScaleFilter, "scale")
REGISTER_FILTER_CLASS_INSTANCE(PulseFilter, "pulse")
REGISTER_FILTER_CLASS_INSTANCE(TranslateFilter, "translate")
REGISTER_FILTER_CLASS_INSTANCE(TransformFilter, "transform")
REGISTER_FILTER_CLASS_INSTANCE(PostTransformFilter, "postTransform")
REGISTER_FILTER_CLASS_INSTANCE(RotateFilter, "rotate")
const QString JSON_FILTER_TYPE = QStringLiteral("type");
const QString JSON_FILTER_PARAMS = QStringLiteral("params");
@ -76,7 +84,6 @@ bool Filter::parseSingleFloatParameter(const QJsonValue& parameters, const QStri
return true;
}
} else if (parameters.isObject()) {
static const QString JSON_MIN = QStringLiteral("interval");
auto objectParameters = parameters.toObject();
if (objectParameters.contains(name)) {
output = objectParameters[name].toDouble();
@ -86,6 +93,92 @@ bool Filter::parseSingleFloatParameter(const QJsonValue& parameters, const QStri
return false;
}
bool Filter::parseVec3Parameter(const QJsonValue& parameters, glm::vec3& output) {
if (parameters.isDouble()) {
output = glm::vec3(parameters.toDouble());
return true;
} else if (parameters.isArray()) {
auto arrayParameters = parameters.toArray();
if (arrayParameters.size() == 3) {
output = glm::vec3(arrayParameters[0].toDouble(),
arrayParameters[1].toDouble(),
arrayParameters[2].toDouble());
return true;
}
} else if (parameters.isObject()) {
auto objectParameters = parameters.toObject();
if (objectParameters.contains("x") && objectParameters.contains("y") && objectParameters.contains("z")) {
output = glm::vec3(objectParameters["x"].toDouble(),
objectParameters["y"].toDouble(),
objectParameters["z"].toDouble());
return true;
}
}
return false;
}
bool Filter::parseMat4Parameter(const QJsonValue& parameters, glm::mat4& output) {
if (parameters.isObject()) {
auto objectParameters = parameters.toObject();
if (objectParameters.contains("r0c0") &&
objectParameters.contains("r1c0") &&
objectParameters.contains("r2c0") &&
objectParameters.contains("r3c0") &&
objectParameters.contains("r0c1") &&
objectParameters.contains("r1c1") &&
objectParameters.contains("r2c1") &&
objectParameters.contains("r3c1") &&
objectParameters.contains("r0c2") &&
objectParameters.contains("r1c2") &&
objectParameters.contains("r2c2") &&
objectParameters.contains("r3c2") &&
objectParameters.contains("r0c3") &&
objectParameters.contains("r1c3") &&
objectParameters.contains("r2c3") &&
objectParameters.contains("r3c3")) {
output[0][0] = objectParameters["r0c0"].toDouble();
output[0][1] = objectParameters["r1c0"].toDouble();
output[0][2] = objectParameters["r2c0"].toDouble();
output[0][3] = objectParameters["r3c0"].toDouble();
output[1][0] = objectParameters["r0c1"].toDouble();
output[1][1] = objectParameters["r1c1"].toDouble();
output[1][2] = objectParameters["r2c1"].toDouble();
output[1][3] = objectParameters["r3c1"].toDouble();
output[2][0] = objectParameters["r0c2"].toDouble();
output[2][1] = objectParameters["r1c2"].toDouble();
output[2][2] = objectParameters["r2c2"].toDouble();
output[2][3] = objectParameters["r3c2"].toDouble();
output[3][0] = objectParameters["r0c3"].toDouble();
output[3][1] = objectParameters["r1c3"].toDouble();
output[3][2] = objectParameters["r2c3"].toDouble();
output[3][3] = objectParameters["r3c3"].toDouble();
return true;
}
}
return false;
}
bool Filter::parseQuatParameter(const QJsonValue& parameters, glm::quat& output) {
if (parameters.isObject()) {
auto objectParameters = parameters.toObject();
if (objectParameters.contains("w") &&
objectParameters.contains("x") &&
objectParameters.contains("y") &&
objectParameters.contains("z")) {
output = glm::quat(objectParameters["w"].toDouble(),
objectParameters["x"].toDouble(),
objectParameters["y"].toDouble(),
objectParameters["z"].toDouble());
return true;
}
}
return false;
}
#if 0

View file

@ -21,6 +21,8 @@
#include <QtCore/QEasingCurve>
#include "../Pose.h"
class QJsonValue;
namespace controller {
@ -34,6 +36,8 @@ namespace controller {
using Factory = hifi::SimpleFactory<Filter, QString>;
virtual float apply(float value) const = 0;
virtual Pose apply(Pose value) const = 0;
// Factory features
virtual bool parseParameters(const QJsonValue& parameters) { return true; }
@ -42,6 +46,9 @@ namespace controller {
static Factory& getFactory() { return _factory; }
static bool parseSingleFloatParameter(const QJsonValue& parameters, const QString& name, float& output);
static bool parseVec3Parameter(const QJsonValue& parameters, glm::vec3& output);
static bool parseQuatParameter(const QJsonValue& parameters, glm::quat& output);
static bool parseMat4Parameter(const QJsonValue& parameters, glm::mat4& output);
protected:
static Factory _factory;
};

View file

@ -26,6 +26,10 @@
#include "filters/InvertFilter.h"
#include "filters/PulseFilter.h"
#include "filters/ScaleFilter.h"
#include "filters/TranslateFilter.h"
#include "filters/TransformFilter.h"
#include "filters/PostTransformFilter.h"
#include "filters/RotateFilter.h"
#include "conditionals/AndConditional.h"
using namespace controller;
@ -103,6 +107,26 @@ QObject* RouteBuilderProxy::deadZone(float min) {
return this;
}
QObject* RouteBuilderProxy::translate(glm::vec3 translate) {
addFilter(std::make_shared<TranslateFilter>(translate));
return this;
}
QObject* RouteBuilderProxy::transform(glm::mat4 transform) {
addFilter(std::make_shared<TransformFilter>(transform));
return this;
}
QObject* RouteBuilderProxy::postTransform(glm::mat4 transform) {
addFilter(std::make_shared<PostTransformFilter>(transform));
return this;
}
QObject* RouteBuilderProxy::rotate(glm::quat rotation) {
addFilter(std::make_shared<RotateFilter>(rotation));
return this;
}
QObject* RouteBuilderProxy::constrainToInteger() {
addFilter(std::make_shared<ConstrainToIntegerFilter>());
return this;

View file

@ -48,6 +48,10 @@ class RouteBuilderProxy : public QObject {
Q_INVOKABLE QObject* deadZone(float min);
Q_INVOKABLE QObject* constrainToInteger();
Q_INVOKABLE QObject* constrainToPositiveInteger();
Q_INVOKABLE QObject* translate(glm::vec3 translate);
Q_INVOKABLE QObject* transform(glm::mat4 transform);
Q_INVOKABLE QObject* postTransform(glm::mat4 transform);
Q_INVOKABLE QObject* rotate(glm::quat rotation);
private:
void to(const Endpoint::Pointer& destination);

View file

@ -21,6 +21,9 @@ public:
virtual float apply(float value) const override {
return glm::clamp(value, _min, _max);
}
virtual Pose apply(Pose value) const override { return value; }
virtual bool parseParameters(const QJsonValue& parameters) override;
protected:
float _min = 0.0f;

View file

@ -22,6 +22,9 @@ public:
virtual float apply(float value) const override {
return glm::sign(value);
}
virtual Pose apply(Pose value) const override { return value; }
protected:
};

View file

@ -22,6 +22,9 @@ public:
virtual float apply(float value) const override {
return (value <= 0.0f) ? 0.0f : 1.0f;
}
virtual Pose apply(Pose value) const override { return value; }
protected:
};

View file

@ -20,6 +20,9 @@ public:
DeadZoneFilter(float min = 0.0) : _min(min) {};
virtual float apply(float value) const override;
virtual Pose apply(Pose value) const override { return value; }
virtual bool parseParameters(const QJsonValue& parameters) override;
protected:
float _min = 0.0f;

View file

@ -19,6 +19,9 @@ class HysteresisFilter : public Filter {
public:
HysteresisFilter(float min = 0.25, float max = 0.75);
virtual float apply(float value) const override;
virtual Pose apply(Pose value) const override { return value; }
virtual bool parseParameters(const QJsonValue& parameters) override;
protected:
float _min;

View file

@ -0,0 +1,33 @@
//
// Created by Brad Hefta-Gaub 2017/04/11
// Copyright 2017 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_Filters_PostTransform_h
#define hifi_Controllers_Filters_PostTransform_h
#include <glm/gtx/transform.hpp>
#include "../Filter.h"
namespace controller {
class PostTransformFilter : public Filter {
REGISTER_FILTER_CLASS(PostTransformFilter);
public:
PostTransformFilter() { }
PostTransformFilter(glm::mat4 transform) : _transform(transform) {}
virtual float apply(float value) const override { return value; }
virtual Pose apply(Pose value) const override { return value.postTransform(_transform); }
virtual bool parseParameters(const QJsonValue& parameters) override { return parseMat4Parameter(parameters, _transform); }
private:
glm::mat4 _transform;
};
}
#endif

View file

@ -23,6 +23,8 @@ public:
virtual float apply(float value) const override;
virtual Pose apply(Pose value) const override { return value; }
virtual bool parseParameters(const QJsonValue& parameters) override;
private:

View file

@ -0,0 +1,39 @@
//
// Created by Brad Hefta-Gaub 2017/04/11
// Copyright 2017 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_Filters_Rotate_h
#define hifi_Controllers_Filters_Rotate_h
#include <glm/gtx/transform.hpp>
#include "../Filter.h"
namespace controller {
class RotateFilter : public Filter {
REGISTER_FILTER_CLASS(RotateFilter);
public:
RotateFilter() { }
RotateFilter(glm::quat rotation) : _rotation(rotation) {}
virtual float apply(float value) const override { return value; }
virtual Pose apply(Pose value) const override {
return value.transform(glm::mat4(glm::quat(_rotation)));
}
virtual bool parseParameters(const QJsonValue& parameters) override { return parseQuatParameter(parameters, _rotation); }
private:
glm::quat _rotation;
};
}
#endif

View file

@ -10,6 +10,8 @@
#ifndef hifi_Controllers_Filters_Scale_h
#define hifi_Controllers_Filters_Scale_h
#include <glm/gtc/matrix_transform.hpp>
#include "../Filter.h"
namespace controller {
@ -23,6 +25,11 @@ public:
virtual float apply(float value) const override {
return value * _scale;
}
virtual Pose apply(Pose value) const override {
return value.transform(glm::scale(glm::mat4(), glm::vec3(_scale)));
}
virtual bool parseParameters(const QJsonValue& parameters) override;
private:

View file

@ -0,0 +1,35 @@
//
// Created by Brad Hefta-Gaub 2017/04/11
// Copyright 2017 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_Filters_Transform_h
#define hifi_Controllers_Filters_Transform_h
#include <glm/gtx/transform.hpp>
#include "../Filter.h"
namespace controller {
class TransformFilter : public Filter {
REGISTER_FILTER_CLASS(TransformFilter);
public:
TransformFilter() { }
TransformFilter(glm::mat4 transform) : _transform(transform) {}
virtual float apply(float value) const override { return value; }
virtual Pose apply(Pose value) const override { return value.transform(_transform); }
virtual bool parseParameters(const QJsonValue& parameters) override { return parseMat4Parameter(parameters, _transform); }
private:
glm::mat4 _transform;
};
}
#endif

View file

@ -0,0 +1,35 @@
//
// Created by Brad Hefta-Gaub 2017/04/11
// Copyright 2017 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_Filters_Translate_h
#define hifi_Controllers_Filters_Translate_h
#include <glm/gtx/transform.hpp>
#include "../Filter.h"
namespace controller {
class TranslateFilter : public Filter {
REGISTER_FILTER_CLASS(TranslateFilter);
public:
TranslateFilter() { }
TranslateFilter(glm::vec3 translate) : _translate(translate) {}
virtual float apply(float value) const override { return value; }
virtual Pose apply(Pose value) const override { return value.transform(glm::translate(_translate)); }
virtual bool parseParameters(const QJsonValue& parameters) override { return parseVec3Parameter(parameters, _translate); }
private:
glm::vec3 _translate { 0.0f };
};
}
#endif

View file

@ -0,0 +1,34 @@
//
// puppetFeet3.js
// examples/controllers
//
// Created by Brad Hefta-Gaub on 2017/04/11
// Copyright 2017 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
//
var MAPPING_NAME = "com.highfidelity.examples.puppetFeet3";
var mapping = Controller.newMapping(MAPPING_NAME);
var puppetOffset = { x: 0, y: -1, z: 0 };
var rotation = Quat.fromPitchYawRollDegrees(0, 0, -90);
var noTranslation = { x: 0, y: 0, z: 0 };
var transformMatrix = Mat4.createFromRotAndTrans(rotation, noTranslation);
var rotateAndTranslate = Mat4.createFromRotAndTrans(rotation, puppetOffset);
mapping.from(Controller.Standard.LeftHand).peek().rotate(rotation).translate(puppetOffset).to(Controller.Standard.LeftFoot);
//mapping.from(Controller.Standard.LeftHand).peek().translate(puppetOffset).to(Controller.Standard.LeftFoot);
//mapping.from(Controller.Standard.LeftHand).peek().transform(transformMatrix).translate(puppetOffset).to(Controller.Standard.LeftFoot);
//mapping.from(Controller.Standard.LeftHand).peek().transform(rotateAndTranslate).to(Controller.Standard.LeftFoot);
Controller.enableMapping(MAPPING_NAME);
Script.scriptEnding.connect(function(){
mapping.disable();
});