mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-08 18:02:13 +02:00
Merge pull request #7285 from hyperlogic/tony/hand-controller-move-improvements
Hand controller mouse improvements
This commit is contained in:
commit
ddb09ec222
12 changed files with 388 additions and 34 deletions
|
@ -10,9 +10,9 @@
|
|||
//
|
||||
|
||||
var DEBUGGING = false;
|
||||
var angularVelocityTrailingAverage = 0.0; // Global trailing average used to decide whether to move reticle at all
|
||||
var angularVelocityTrailingAverage = 0.0; // Global trailing average used to decide whether to move reticle at all
|
||||
var lastX = 0;
|
||||
var lastY = 0;
|
||||
var lastY = 0;
|
||||
|
||||
Math.clamp=function(a,b,c) {
|
||||
return Math.max(b,Math.min(c,a));
|
||||
|
@ -54,9 +54,11 @@ function debugPrint(message) {
|
|||
var leftRightBias = 0.0;
|
||||
var filteredRotatedLeft = Vec3.UNIT_NEG_Y;
|
||||
var filteredRotatedRight = Vec3.UNIT_NEG_Y;
|
||||
var lastAlpha = 0;
|
||||
|
||||
Script.update.connect(function(deltaTime) {
|
||||
|
||||
// avatar frame
|
||||
var poseRight = Controller.getPoseValue(Controller.Standard.RightHand);
|
||||
var poseLeft = Controller.getPoseValue(Controller.Standard.LeftHand);
|
||||
|
||||
|
@ -65,53 +67,59 @@ Script.update.connect(function(deltaTime) {
|
|||
var screenSizeX = screenSize.x;
|
||||
var screenSizeY = screenSize.y;
|
||||
|
||||
var rotatedRight = Vec3.multiplyQbyV(poseRight.rotation, Vec3.UNIT_NEG_Y);
|
||||
var rotatedLeft = Vec3.multiplyQbyV(poseLeft.rotation, Vec3.UNIT_NEG_Y);
|
||||
// transform hand facing vectors from avatar frame into sensor frame.
|
||||
var worldToSensorMatrix = Mat4.inverse(MyAvatar.sensorToWorldMatrix);
|
||||
var rotatedRight = Mat4.transformVector(worldToSensorMatrix, Vec3.multiplyQbyV(MyAvatar.orientation, Vec3.multiplyQbyV(poseRight.rotation, Vec3.UNIT_NEG_Y)));
|
||||
var rotatedLeft = Mat4.transformVector(worldToSensorMatrix, Vec3.multiplyQbyV(MyAvatar.orientation, Vec3.multiplyQbyV(poseLeft.rotation, Vec3.UNIT_NEG_Y)));
|
||||
|
||||
lastRotatedRight = rotatedRight;
|
||||
|
||||
|
||||
// Decide which hand should be controlling the pointer
|
||||
// by comparing which one is moving more, and by
|
||||
// tending to stay with the one moving more.
|
||||
var BIAS_ADJUST_RATE = 0.5;
|
||||
var BIAS_ADJUST_DEADZONE = 0.05;
|
||||
leftRightBias += (Vec3.length(poseRight.angularVelocity) - Vec3.length(poseLeft.angularVelocity)) * BIAS_ADJUST_RATE;
|
||||
if (leftRightBias < BIAS_ADJUST_DEADZONE) {
|
||||
leftRightBias = 0.0;
|
||||
} else if (leftRightBias > (1.0 - BIAS_ADJUST_DEADZONE)) {
|
||||
leftRightBias = 1.0;
|
||||
// by comparing which one is moving more, and by
|
||||
// tending to stay with the one moving more.
|
||||
if (deltaTime > 0.001) {
|
||||
// leftRightBias is a running average of the difference in angular hand speed.
|
||||
// a positive leftRightBias indicates the right hand is spinning faster then the left hand.
|
||||
// a negative leftRightBias indicates the left hand is spnning faster.
|
||||
var BIAS_ADJUST_PERIOD = 1.0;
|
||||
var tau = Math.clamp(deltaTime / BIAS_ADJUST_PERIOD, 0, 1);
|
||||
newLeftRightBias = Vec3.length(poseRight.angularVelocity) - Vec3.length(poseLeft.angularVelocity);
|
||||
leftRightBias = (1 - tau) * leftRightBias + tau * newLeftRightBias;
|
||||
}
|
||||
|
||||
// add a bit of hysteresis to prevent control flopping back and forth
|
||||
// between hands when they are both mostly stationary.
|
||||
var alpha;
|
||||
var HYSTERESIS_OFFSET = 0.25;
|
||||
if (lastAlpha > 0.5) {
|
||||
// prefer right hand over left
|
||||
alpha = leftRightBias > -HYSTERESIS_OFFSET ? 1 : 0;
|
||||
} else {
|
||||
alpha = leftRightBias > HYSTERESIS_OFFSET ? 1 : 0;
|
||||
}
|
||||
lastAlpha = alpha;
|
||||
|
||||
// Velocity filter the hand rotation used to position reticle so that it is easier to target small things with the hand controllers
|
||||
var VELOCITY_FILTER_GAIN = 0.5;
|
||||
filteredRotatedLeft = Vec3.mix(filteredRotatedLeft, rotatedLeft, Math.clamp(Vec3.length(poseLeft.angularVelocity) * VELOCITY_FILTER_GAIN, 0.0, 1.0));
|
||||
filteredRotatedRight = Vec3.mix(filteredRotatedRight, rotatedRight, Math.clamp(Vec3.length(poseRight.angularVelocity) * VELOCITY_FILTER_GAIN, 0.0, 1.0));
|
||||
var rotated = Vec3.mix(filteredRotatedLeft, filteredRotatedRight, leftRightBias);
|
||||
var rotated = Vec3.mix(filteredRotatedLeft, filteredRotatedRight, alpha);
|
||||
|
||||
var absolutePitch = rotated.y; // from 1 down to -1 up ... but note: if you rotate down "too far" it starts to go up again...
|
||||
var absoluteYaw = -rotated.x; // from -1 left to 1 right
|
||||
|
||||
var ROTATION_BOUND = 0.6;
|
||||
var clampYaw = Math.clamp(absoluteYaw, -ROTATION_BOUND, ROTATION_BOUND);
|
||||
var clampPitch = Math.clamp(absolutePitch, -ROTATION_BOUND, ROTATION_BOUND);
|
||||
|
||||
// using only from -ROTATION_BOUND to ROTATION_BOUND
|
||||
var xRatio = (clampYaw + ROTATION_BOUND) / (2 * ROTATION_BOUND);
|
||||
var yRatio = (clampPitch + ROTATION_BOUND) / (2 * ROTATION_BOUND);
|
||||
|
||||
var x = screenSizeX * xRatio;
|
||||
var y = screenSizeY * yRatio;
|
||||
var x = Math.clamp(screenSizeX * (absoluteYaw + 0.5), 0, screenSizeX);
|
||||
var y = Math.clamp(screenSizeX * absolutePitch, 0, screenSizeY);
|
||||
|
||||
// don't move the reticle with the hand controllers unless the controllers are actually being moved
|
||||
// take a time average of angular velocity, and don't move mouse at all if it's below threshold
|
||||
|
||||
|
||||
var AVERAGING_INTERVAL = 0.95;
|
||||
var MINIMUM_CONTROLLER_ANGULAR_VELOCITY = 0.03;
|
||||
var angularVelocityMagnitude = Vec3.length(poseLeft.angularVelocity) * (1.0 - leftRightBias) + Vec3.length(poseRight.angularVelocity) * leftRightBias;
|
||||
var angularVelocityMagnitude = Vec3.length(poseLeft.angularVelocity) * (1.0 - alpha) + Vec3.length(poseRight.angularVelocity) * alpha;
|
||||
angularVelocityTrailingAverage = angularVelocityTrailingAverage * AVERAGING_INTERVAL + angularVelocityMagnitude * (1.0 - AVERAGING_INTERVAL);
|
||||
|
||||
if (!(xRatio == 0.5 && yRatio == 0) && (angularVelocityTrailingAverage > MINIMUM_CONTROLLER_ANGULAR_VELOCITY) && ((x != lastX) || (y != lastY))) {
|
||||
if ((angularVelocityTrailingAverage > MINIMUM_CONTROLLER_ANGULAR_VELOCITY) && ((x != lastX) || (y != lastY))) {
|
||||
moveReticleAbsolute(x, y);
|
||||
lastX = x;
|
||||
lastY = y;
|
||||
|
@ -121,5 +129,3 @@ Script.update.connect(function(deltaTime) {
|
|||
Script.scriptEnding.connect(function(){
|
||||
mapping.disable();
|
||||
});
|
||||
|
||||
|
||||
|
|
165
examples/tests/mat4test.js
Normal file
165
examples/tests/mat4test.js
Normal file
|
@ -0,0 +1,165 @@
|
|||
//
|
||||
// mat4test.js
|
||||
// examples/tests
|
||||
//
|
||||
// Created by Anthony Thibault on 2016/3/7
|
||||
// Copyright 2016 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 IDENTITY = {r0c0: 1, r0c1: 0, r0c2: 0, r0c3: 0,
|
||||
r1c0: 0, r1c1: 1, r1c2: 0, r1c3: 0,
|
||||
r2c0: 0, r2c1: 0, r2c2: 1, r2c3: 0,
|
||||
r3c0: 0, r3c1: 0, r3c2: 0, r3c3: 1};
|
||||
|
||||
var ROT_ZERO = {x: 0, y: 0, z: 0, w: 1};
|
||||
var ROT_Y_180 = {x: 0, y: 1, z: 0, w: 0};
|
||||
|
||||
var ONE = {x: 1, y: 1, z: 1};
|
||||
var ZERO = {x: 0, y: 0, z: 0};
|
||||
var ONE_TWO_THREE = {x: 1, y: 2, z: 3};
|
||||
var ONE_HALF = {x: 0.5, y: 0.5, z: 0.5};
|
||||
|
||||
var EPSILON = 0.000001;
|
||||
|
||||
function mat4FuzzyEqual(a, b) {
|
||||
var r, c;
|
||||
for (r = 0; r < 4; r++) {
|
||||
for (c = 0; c < 4; c++) {
|
||||
if (Math.abs(a["r" + r + "c" + c] - b["r" + r + "c" + c]) > EPSILON) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function vec3FuzzyEqual(a, b) {
|
||||
if (Math.abs(a.x - b.x) > EPSILON ||
|
||||
Math.abs(a.y - b.y) > EPSILON ||
|
||||
Math.abs(a.z - b.z) > EPSILON) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function quatFuzzyEqual(a, b) {
|
||||
if (Math.abs(a.x - b.x) > EPSILON ||
|
||||
Math.abs(a.y - b.y) > EPSILON ||
|
||||
Math.abs(a.z - b.z) > EPSILON ||
|
||||
Math.abs(a.w - b.w) > EPSILON) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
var failureCount = 0;
|
||||
var testCount = 0;
|
||||
function assert(test) {
|
||||
testCount++;
|
||||
if (!test) {
|
||||
print("MAT4 TEST " + testCount + " failed!");
|
||||
failureCount++;
|
||||
}
|
||||
}
|
||||
|
||||
function testCreate() {
|
||||
var test0 = Mat4.createFromScaleRotAndTrans(ONE, {x: 0, y: 0, z: 0, w: 1}, ZERO);
|
||||
assert(mat4FuzzyEqual(test0, IDENTITY));
|
||||
|
||||
var test1 = Mat4.createFromRotAndTrans({x: 0, y: 0, z: 0, w: 1}, ZERO);
|
||||
assert(mat4FuzzyEqual(test1, IDENTITY));
|
||||
|
||||
var test2 = Mat4.createFromRotAndTrans(ROT_Y_180, ONE_TWO_THREE);
|
||||
assert(mat4FuzzyEqual(test2, {r0c0: -1, r0c1: 0, r0c2: 0, r0c3: 1,
|
||||
r1c0: 0, r1c1: 1, r1c2: 0, r1c3: 2,
|
||||
r2c0: 0, r2c1: 0, r2c2: -1, r2c3: 3,
|
||||
r3c0: 0, r3c1: 0, r3c2: 0, r3c3: 1}));
|
||||
|
||||
var test3 = Mat4.createFromScaleRotAndTrans(ONE_HALF, ROT_Y_180, ONE_TWO_THREE);
|
||||
assert(mat4FuzzyEqual(test3, {r0c0: -0.5, r0c1: 0, r0c2: 0, r0c3: 1,
|
||||
r1c0: 0, r1c1: 0.5, r1c2: 0, r1c3: 2,
|
||||
r2c0: 0, r2c1: 0, r2c2: -0.5, r2c3: 3,
|
||||
r3c0: 0, r3c1: 0, r3c2: 0, r3c3: 1}));
|
||||
}
|
||||
|
||||
function testExtractTranslation() {
|
||||
var test0 = Mat4.extractTranslation(IDENTITY);
|
||||
assert(vec3FuzzyEqual(ZERO, test0));
|
||||
|
||||
var test1 = Mat4.extractTranslation(Mat4.createFromRotAndTrans(ROT_Y_180, ONE_TWO_THREE));
|
||||
assert(vec3FuzzyEqual(ONE_TWO_THREE, test1));
|
||||
}
|
||||
|
||||
function testExtractRotation() {
|
||||
var test0 = Mat4.extractRotation(IDENTITY);
|
||||
assert(quatFuzzyEqual(ROT_ZERO, test0));
|
||||
|
||||
var test1 = Mat4.extractRotation(Mat4.createFromRotAndTrans(ROT_Y_180, ONE_TWO_THREE));
|
||||
assert(quatFuzzyEqual(ROT_Y_180, test1));
|
||||
}
|
||||
|
||||
function testExtractScale() {
|
||||
var test0 = Mat4.extractScale(IDENTITY);
|
||||
assert(vec3FuzzyEqual(ONE, test0));
|
||||
|
||||
var test1 = Mat4.extractScale(Mat4.createFromScaleRotAndTrans(ONE_HALF, ROT_Y_180, ONE_TWO_THREE));
|
||||
assert(vec3FuzzyEqual(ONE_HALF, test1));
|
||||
|
||||
var test2 = Mat4.extractScale(Mat4.createFromScaleRotAndTrans(ONE_TWO_THREE, ROT_ZERO, ONE_TWO_THREE));
|
||||
assert(vec3FuzzyEqual(ONE_TWO_THREE, test2));
|
||||
}
|
||||
|
||||
function testTransformPoint() {
|
||||
var test0 = Mat4.transformPoint(IDENTITY, ONE);
|
||||
assert(vec3FuzzyEqual(ONE, test0));
|
||||
|
||||
var m = Mat4.createFromScaleRotAndTrans(ONE_HALF, ROT_Y_180, ONE_TWO_THREE);
|
||||
var test1 = Mat4.transformPoint(m, ONE);
|
||||
assert(vec3FuzzyEqual({x: 0.5, y: 2.5, z: 2.5}, test1));
|
||||
}
|
||||
|
||||
function testTransformVector() {
|
||||
var test0 = Mat4.transformVector(IDENTITY, ONE);
|
||||
assert(vec3FuzzyEqual(ONE, test0));
|
||||
|
||||
var m = Mat4.createFromScaleRotAndTrans(ONE_HALF, ROT_Y_180, ONE_TWO_THREE);
|
||||
var test1 = Mat4.transformVector(m, ONE);
|
||||
assert(vec3FuzzyEqual({x: -0.5, y: 0.5, z: -0.5}, test1));
|
||||
}
|
||||
|
||||
function testInverse() {
|
||||
var test0 = IDENTITY;
|
||||
assert(mat4FuzzyEqual(IDENTITY, Mat4.multiply(test0, Mat4.inverse(test0))));
|
||||
|
||||
var test1 = Mat4.createFromScaleRotAndTrans(ONE_HALF, ROT_Y_180, ONE_TWO_THREE);
|
||||
assert(mat4FuzzyEqual(IDENTITY, Mat4.multiply(test1, Mat4.inverse(test1))));
|
||||
|
||||
var test2 = Mat4.extractScale(Mat4.createFromScaleRotAndTrans(ONE_TWO_THREE, ROT_ZERO, ONE_TWO_THREE));
|
||||
assert(mat4FuzzyEqual(IDENTITY, Mat4.multiply(test2, Mat4.inverse(test2))));
|
||||
}
|
||||
|
||||
function testFront() {
|
||||
var test0 = IDENTITY;
|
||||
assert(mat4FuzzyEqual({x: 0, y: 0, z: -1}, Mat4.getFront(test0)));
|
||||
|
||||
var test1 = Mat4.createFromScaleRotAndTrans(ONE_HALF, ROT_Y_180, ONE_TWO_THREE);
|
||||
assert(mat4FuzzyEqual({x: 0, y: 0, z: 1}, Mat4.getFront(test1)));
|
||||
}
|
||||
|
||||
function testMat4() {
|
||||
testCreate();
|
||||
testExtractTranslation();
|
||||
testExtractRotation();
|
||||
testExtractScale();
|
||||
testTransformPoint();
|
||||
testTransformVector();
|
||||
testInverse();
|
||||
testFront();
|
||||
|
||||
print("MAT4 TEST complete! (" + (testCount - failureCount) + "/" + testCount + ") tests passed!");
|
||||
}
|
||||
|
||||
testMat4();
|
|
@ -416,8 +416,9 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
}
|
||||
}
|
||||
|
||||
// thread-safe
|
||||
glm::mat4 MyAvatar::getSensorToWorldMatrix() const {
|
||||
return _sensorToWorldMatrix;
|
||||
return _sensorToWorldMatrixCache.get();
|
||||
}
|
||||
|
||||
// Pass a recent sample of the HMD to the avatar.
|
||||
|
@ -442,6 +443,8 @@ void MyAvatar::updateSensorToWorldMatrix() {
|
|||
_sensorToWorldMatrix = desiredMat * glm::inverse(_bodySensorMatrix);
|
||||
|
||||
lateUpdatePalms();
|
||||
|
||||
_sensorToWorldMatrixCache.set(_sensorToWorldMatrix);
|
||||
}
|
||||
|
||||
// Update avatar head rotation with sensor data
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#include "Avatar.h"
|
||||
#include "AtRestDetector.h"
|
||||
#include "MyCharacterController.h"
|
||||
|
||||
#include <ThreadSafeValueCache.h>
|
||||
|
||||
class ModelItemID;
|
||||
|
||||
|
@ -78,6 +78,9 @@ class MyAvatar : public Avatar {
|
|||
Q_PROPERTY(controller::Pose rightHandPose READ getRightHandPose)
|
||||
Q_PROPERTY(controller::Pose leftHandTipPose READ getLeftHandTipPose)
|
||||
Q_PROPERTY(controller::Pose rightHandTipPose READ getRightHandTipPose)
|
||||
|
||||
Q_PROPERTY(glm::mat4 sensorToWorldMatrix READ getSensorToWorldMatrix)
|
||||
|
||||
Q_PROPERTY(float energy READ getEnergy WRITE setEnergy)
|
||||
|
||||
public:
|
||||
|
@ -98,8 +101,9 @@ public:
|
|||
const glm::vec3& getHMDSensorPosition() const { return _hmdSensorPosition; }
|
||||
const glm::quat& getHMDSensorOrientation() const { return _hmdSensorOrientation; }
|
||||
const glm::vec2& getHMDSensorFacingMovingAverage() const { return _hmdSensorFacingMovingAverage; }
|
||||
glm::mat4 getSensorToWorldMatrix() const;
|
||||
|
||||
// thread safe
|
||||
Q_INVOKABLE glm::mat4 getSensorToWorldMatrix() const;
|
||||
|
||||
// Pass a recent sample of the HMD to the avatar.
|
||||
// This can also update the avatar's position to follow the HMD
|
||||
|
@ -390,6 +394,7 @@ private:
|
|||
|
||||
// used to transform any sensor into world space, including the _hmdSensorMat, or hand controllers.
|
||||
glm::mat4 _sensorToWorldMatrix;
|
||||
ThreadSafeValueCache<glm::mat4> _sensorToWorldMatrixCache { glm::mat4() };
|
||||
|
||||
struct FollowHelper {
|
||||
FollowHelper();
|
||||
|
|
71
libraries/script-engine/src/Mat4.cpp
Normal file
71
libraries/script-engine/src/Mat4.cpp
Normal file
|
@ -0,0 +1,71 @@
|
|||
//
|
||||
// Mat4.cpp
|
||||
// libraries/script-engine/src
|
||||
//
|
||||
// Created by Anthony Thibault on 3/7/16.
|
||||
// Copyright 2016 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 <QDebug>
|
||||
#include <GLMHelpers.h>
|
||||
#include "ScriptEngineLogging.h"
|
||||
#include "Mat4.h"
|
||||
|
||||
glm::mat4 Mat4::multiply(const glm::mat4& m1, const glm::mat4& m2) const {
|
||||
return m1 * m2;
|
||||
}
|
||||
|
||||
glm::mat4 Mat4::createFromRotAndTrans(const glm::quat& rot, const glm::vec3& trans) const {
|
||||
return ::createMatFromQuatAndPos(rot, trans);
|
||||
}
|
||||
|
||||
glm::mat4 Mat4::createFromScaleRotAndTrans(const glm::vec3& scale, const glm::quat& rot, const glm::vec3& trans) const {
|
||||
return createMatFromScaleQuatAndPos(scale, rot, trans);
|
||||
}
|
||||
|
||||
glm::vec3 Mat4::extractTranslation(const glm::mat4& m) const {
|
||||
return ::extractTranslation(m);
|
||||
}
|
||||
|
||||
glm::quat Mat4::extractRotation(const glm::mat4& m) const {
|
||||
return ::glmExtractRotation(m);
|
||||
}
|
||||
|
||||
glm::vec3 Mat4::extractScale(const glm::mat4& m) const {
|
||||
return ::extractScale(m);
|
||||
}
|
||||
|
||||
glm::vec3 Mat4::transformPoint(const glm::mat4& m, const glm::vec3& point) const {
|
||||
return ::transformPoint(m, point);
|
||||
}
|
||||
|
||||
glm::vec3 Mat4::transformVector(const glm::mat4& m, const glm::vec3& vector) const {
|
||||
return ::transformVectorFast(m, vector);
|
||||
}
|
||||
|
||||
glm::mat4 Mat4::inverse(const glm::mat4& m) const {
|
||||
return glm::inverse(m);
|
||||
}
|
||||
|
||||
glm::vec3 Mat4::getFront(const glm::mat4& m) const {
|
||||
return glm::vec3(-m[0][2], -m[1][2], -m[2][2]);
|
||||
}
|
||||
|
||||
glm::vec3 Mat4::getRight(const glm::mat4& m) const {
|
||||
return glm::vec3(m[0][0], m[1][0], m[2][0]);
|
||||
}
|
||||
|
||||
glm::vec3 Mat4::getUp(const glm::mat4& m) const {
|
||||
return glm::vec3(m[0][1], m[1][1], m[2][1]);
|
||||
}
|
||||
|
||||
void Mat4::print(const QString& label, const glm::mat4& m) const {
|
||||
qCDebug(scriptengine) << qPrintable(label) <<
|
||||
"row0 =" << m[0][0] << "," << m[1][0] << "," << m[2][0] << "," << m[3][0] <<
|
||||
"row1 =" << m[0][1] << "," << m[1][1] << "," << m[2][1] << "," << m[3][1] <<
|
||||
"row2 =" << m[0][2] << "," << m[1][2] << "," << m[2][2] << "," << m[3][2] <<
|
||||
"row3 =" << m[0][3] << "," << m[1][3] << "," << m[2][3] << "," << m[3][3];
|
||||
}
|
45
libraries/script-engine/src/Mat4.h
Normal file
45
libraries/script-engine/src/Mat4.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
//
|
||||
// Mat4.h
|
||||
// libraries/script-engine/src
|
||||
//
|
||||
// Created by Anthony Thibault on 3/7/16.
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// Scriptable 4x4 Matrix class library.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_Mat4_h
|
||||
#define hifi_Mat4_h
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
/// Scriptable Mat4 object. Used exclusively in the JavaScript API
|
||||
class Mat4 : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public slots:
|
||||
glm::mat4 multiply(const glm::mat4& m1, const glm::mat4& m2) const;
|
||||
glm::mat4 createFromRotAndTrans(const glm::quat& rot, const glm::vec3& trans) const;
|
||||
glm::mat4 createFromScaleRotAndTrans(const glm::vec3& scale, const glm::quat& rot, const glm::vec3& trans) const;
|
||||
|
||||
glm::vec3 extractTranslation(const glm::mat4& m) const;
|
||||
glm::quat extractRotation(const glm::mat4& m) const;
|
||||
glm::vec3 extractScale(const glm::mat4& m) const;
|
||||
|
||||
glm::vec3 transformPoint(const glm::mat4& m, const glm::vec3& point) const;
|
||||
glm::vec3 transformVector(const glm::mat4& m, const glm::vec3& vector) const;
|
||||
|
||||
glm::mat4 inverse(const glm::mat4& m) const;
|
||||
|
||||
glm::vec3 getFront(const glm::mat4& m) const;
|
||||
glm::vec3 getRight(const glm::mat4& m) const;
|
||||
glm::vec3 getUp(const glm::mat4& m) const;
|
||||
|
||||
void print(const QString& label, const glm::mat4& m) const;
|
||||
};
|
||||
|
||||
#endif // hifi_Mat4_h
|
|
@ -315,6 +315,7 @@ void ScriptEngine::init() {
|
|||
registerGlobalObject("Entities", entityScriptingInterface.data());
|
||||
registerGlobalObject("Quat", &_quatLibrary);
|
||||
registerGlobalObject("Vec3", &_vec3Library);
|
||||
registerGlobalObject("Mat4", &_mat4Library);
|
||||
registerGlobalObject("Uuid", &_uuidLibrary);
|
||||
registerGlobalObject("AnimationCache", DependencyManager::get<AnimationCache>().data());
|
||||
registerGlobalObject("Messages", DependencyManager::get<MessagesClient>().data());
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "ArrayBufferClass.h"
|
||||
#include "AudioScriptingInterface.h"
|
||||
#include "Quat.h"
|
||||
#include "Mat4.h"
|
||||
#include "ScriptCache.h"
|
||||
#include "ScriptUUID.h"
|
||||
#include "Vec3.h"
|
||||
|
@ -199,6 +200,7 @@ protected:
|
|||
QString _fileNameString;
|
||||
Quat _quatLibrary;
|
||||
Vec3 _vec3Library;
|
||||
Mat4 _mat4Library;
|
||||
ScriptUUID _uuidLibrary;
|
||||
std::atomic<bool> _isUserLoaded { false };
|
||||
bool _isReloading { false };
|
||||
|
@ -219,4 +221,4 @@ protected:
|
|||
static std::atomic<bool> _stoppingAllScripts;
|
||||
};
|
||||
|
||||
#endif // hifi_ScriptEngine_h
|
||||
#endif // hifi_ScriptEngine_h
|
||||
|
|
|
@ -376,6 +376,15 @@ glm::mat4 createMatFromQuatAndPos(const glm::quat& q, const glm::vec3& p) {
|
|||
return m;
|
||||
}
|
||||
|
||||
// create matrix from a non-uniform scale, orientation and position
|
||||
glm::mat4 createMatFromScaleQuatAndPos(const glm::vec3& scale, const glm::quat& rot, const glm::vec3& trans) {
|
||||
glm::vec3 xAxis = rot * glm::vec3(scale.x, 0.0f, 0.0f);
|
||||
glm::vec3 yAxis = rot * glm::vec3(0.0f, scale.y, 0.0f);
|
||||
glm::vec3 zAxis = rot * glm::vec3(0.0f, 0.0f, scale.z);
|
||||
return glm::mat4(glm::vec4(xAxis, 0.0f), glm::vec4(yAxis, 0.0f),
|
||||
glm::vec4(zAxis, 0.0f), glm::vec4(trans, 1.0f));
|
||||
}
|
||||
|
||||
// cancel out roll and pitch
|
||||
glm::quat cancelOutRollAndPitch(const glm::quat& q) {
|
||||
glm::vec3 zAxis = q * glm::vec3(0.0f, 0.0f, 1.0f);
|
||||
|
|
|
@ -212,6 +212,7 @@ glm::detail::tvec4<T, P> lerp(const glm::detail::tvec4<T, P>& x, const glm::deta
|
|||
}
|
||||
|
||||
glm::mat4 createMatFromQuatAndPos(const glm::quat& q, const glm::vec3& p);
|
||||
glm::mat4 createMatFromScaleQuatAndPos(const glm::vec3& scale, const glm::quat& rot, const glm::vec3& trans);
|
||||
glm::quat cancelOutRollAndPitch(const glm::quat& q);
|
||||
glm::mat4 cancelOutRollAndPitch(const glm::mat4& m);
|
||||
glm::vec3 transformPoint(const glm::mat4& m, const glm::vec3& p);
|
||||
|
|
|
@ -34,6 +34,7 @@ static int collisionMetaTypeId = qRegisterMetaType<Collision>();
|
|||
static int qMapURLStringMetaTypeId = qRegisterMetaType<QMap<QUrl,QString>>();
|
||||
|
||||
void registerMetaTypes(QScriptEngine* engine) {
|
||||
qScriptRegisterMetaType(engine, mat4toScriptValue, mat4FromScriptValue);
|
||||
qScriptRegisterMetaType(engine, vec4toScriptValue, vec4FromScriptValue);
|
||||
qScriptRegisterMetaType(engine, vec3toScriptValue, vec3FromScriptValue);
|
||||
qScriptRegisterMetaType(engine, qVectorVec3ToScriptValue, qVectorVec3FromScriptValue);
|
||||
|
@ -53,6 +54,46 @@ void registerMetaTypes(QScriptEngine* engine) {
|
|||
qScriptRegisterMetaType(engine, aaCubeToScriptValue, aaCubeFromScriptValue);
|
||||
}
|
||||
|
||||
QScriptValue mat4toScriptValue(QScriptEngine* engine, const glm::mat4& mat4) {
|
||||
QScriptValue obj = engine->newObject();
|
||||
obj.setProperty("r0c0", mat4[0][0]);
|
||||
obj.setProperty("r1c0", mat4[0][1]);
|
||||
obj.setProperty("r2c0", mat4[0][2]);
|
||||
obj.setProperty("r3c0", mat4[0][3]);
|
||||
obj.setProperty("r0c1", mat4[1][0]);
|
||||
obj.setProperty("r1c1", mat4[1][1]);
|
||||
obj.setProperty("r2c1", mat4[1][2]);
|
||||
obj.setProperty("r3c1", mat4[1][3]);
|
||||
obj.setProperty("r0c2", mat4[2][0]);
|
||||
obj.setProperty("r1c2", mat4[2][1]);
|
||||
obj.setProperty("r2c2", mat4[2][2]);
|
||||
obj.setProperty("r3c2", mat4[2][3]);
|
||||
obj.setProperty("r0c3", mat4[3][0]);
|
||||
obj.setProperty("r1c3", mat4[3][1]);
|
||||
obj.setProperty("r2c3", mat4[3][2]);
|
||||
obj.setProperty("r3c3", mat4[3][3]);
|
||||
return obj;
|
||||
}
|
||||
|
||||
void mat4FromScriptValue(const QScriptValue& object, glm::mat4& mat4) {
|
||||
mat4[0][0] = object.property("r0c0").toVariant().toFloat();
|
||||
mat4[0][1] = object.property("r1c0").toVariant().toFloat();
|
||||
mat4[0][2] = object.property("r2c0").toVariant().toFloat();
|
||||
mat4[0][3] = object.property("r3c0").toVariant().toFloat();
|
||||
mat4[1][0] = object.property("r0c1").toVariant().toFloat();
|
||||
mat4[1][1] = object.property("r1c1").toVariant().toFloat();
|
||||
mat4[1][2] = object.property("r2c1").toVariant().toFloat();
|
||||
mat4[1][3] = object.property("r3c1").toVariant().toFloat();
|
||||
mat4[2][0] = object.property("r0c2").toVariant().toFloat();
|
||||
mat4[2][1] = object.property("r1c2").toVariant().toFloat();
|
||||
mat4[2][2] = object.property("r2c2").toVariant().toFloat();
|
||||
mat4[2][3] = object.property("r3c2").toVariant().toFloat();
|
||||
mat4[3][0] = object.property("r0c3").toVariant().toFloat();
|
||||
mat4[3][1] = object.property("r1c3").toVariant().toFloat();
|
||||
mat4[3][2] = object.property("r2c3").toVariant().toFloat();
|
||||
mat4[3][3] = object.property("r3c3").toVariant().toFloat();
|
||||
}
|
||||
|
||||
QScriptValue vec4toScriptValue(QScriptEngine* engine, const glm::vec4& vec4) {
|
||||
QScriptValue obj = engine->newObject();
|
||||
obj.setProperty("x", vec4.x);
|
||||
|
|
|
@ -28,6 +28,7 @@ Q_DECLARE_METATYPE(glm::vec4)
|
|||
Q_DECLARE_METATYPE(glm::vec3)
|
||||
Q_DECLARE_METATYPE(glm::vec2)
|
||||
Q_DECLARE_METATYPE(glm::quat)
|
||||
Q_DECLARE_METATYPE(glm::mat4)
|
||||
Q_DECLARE_METATYPE(xColor)
|
||||
Q_DECLARE_METATYPE(QVector<glm::vec3>)
|
||||
Q_DECLARE_METATYPE(QVector<float>)
|
||||
|
@ -35,6 +36,10 @@ Q_DECLARE_METATYPE(AACube)
|
|||
|
||||
void registerMetaTypes(QScriptEngine* engine);
|
||||
|
||||
// Mat4
|
||||
QScriptValue mat4toScriptValue(QScriptEngine* engine, const glm::mat4& mat4);
|
||||
void mat4FromScriptValue(const QScriptValue& object, glm::mat4& mat4);
|
||||
|
||||
// Vec4
|
||||
QScriptValue vec4toScriptValue(QScriptEngine* engine, const glm::vec4& vec4);
|
||||
void vec4FromScriptValue(const QScriptValue& object, glm::vec4& vec4);
|
||||
|
|
Loading…
Reference in a new issue