mirror of
https://github.com/lubosz/overte.git
synced 2025-04-08 14:42:19 +02:00
Merge pull request #10196 from ZappoMan/poseFilters
support for input controller pose filters
This commit is contained in:
commit
b82a333a5e
19 changed files with 361 additions and 1 deletions
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
};
|
||||
|
||||
|
|
|
@ -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:
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
|
@ -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:
|
||||
|
|
|
@ -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
|
|
@ -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:
|
||||
|
|
|
@ -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
|
|
@ -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
|
34
script-archive/controllers/puppetFeet3.js
Normal file
34
script-archive/controllers/puppetFeet3.js
Normal 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();
|
||||
});
|
Loading…
Reference in a new issue