Skip to content
Snippets Groups Projects
Commit 21de9056 authored by johnshaughnessy's avatar johnshaughnessy
Browse files

Add gyro look controls and combine with finger movement for camera

parent f1f41ff0
No related branches found
No related tags found
No related merge requests found
...@@ -12,6 +12,13 @@ AFRAME.registerComponent("pitch-yaw-rotator", { ...@@ -12,6 +12,13 @@ AFRAME.registerComponent("pitch-yaw-rotator", {
init() { init() {
this.pitch = 0; this.pitch = 0;
this.yaw = 0; this.yaw = 0;
this.onRotateX = this.onRotateX.bind(this);
this.el.sceneEl.addEventListener("rotateX", this.onRotateX);
this.pendingXRotation = 0;
},
onRotateX(e) {
this.pendingXRotation += e.detail.value;
}, },
look(deltaPitch, deltaYaw) { look(deltaPitch, deltaYaw) {
...@@ -30,10 +37,17 @@ AFRAME.registerComponent("pitch-yaw-rotator", { ...@@ -30,10 +37,17 @@ AFRAME.registerComponent("pitch-yaw-rotator", {
tick() { tick() {
const userinput = AFRAME.scenes[0].systems.userinput; const userinput = AFRAME.scenes[0].systems.userinput;
const cameraDelta = userinput.readFrameValueAtPath(paths.actions.cameraDelta); const cameraDelta = userinput.readFrameValueAtPath(paths.actions.cameraDelta);
let lookX = this.pendingXRotation;
let lookY = 0;
if (cameraDelta) { if (cameraDelta) {
this.look(cameraDelta[1], cameraDelta[0]); lookY += cameraDelta[0];
lookX += cameraDelta[1];
}
if (lookX !== 0 || lookY !== 0) {
this.look(lookX, lookY);
this.el.object3D.rotation.set(degToRad(this.pitch), degToRad(this.yaw), 0); this.el.object3D.rotation.set(degToRad(this.pitch), degToRad(this.yaw), 0);
this.el.object3D.rotation.order = "YXZ"; this.el.object3D.rotation.order = "YXZ";
} }
this.pendingXRotation = 0;
} }
}); });
...@@ -4,6 +4,14 @@ import { xforms } from "./xforms"; ...@@ -4,6 +4,14 @@ import { xforms } from "./xforms";
const zero = "/vars/touchscreen/zero"; const zero = "/vars/touchscreen/zero";
const forward = "/vars/touchscreen/pinchDeltaForward"; const forward = "/vars/touchscreen/pinchDeltaForward";
const touchCamDelta = "vars/touchscreen/touchCameraDelta";
const touchCamDeltaX = "vars/touchscreen/touchCameraDelta/x";
const touchCamDeltaY = "vars/touchscreen/touchCameraDelta/y";
const touchCamDeltaXScaled = "vars/touchscreen/touchCameraDelta/x/scaled";
const touchCamDeltaYScaled = "vars/touchscreen/touchCameraDelta/y/scaled";
const gyroCamDelta = "vars/gyro/gyroCameraDelta";
const gyroCamDeltaXScaled = "vars/gyro/gyroCameraDelta/x/scaled";
const gyroCamDeltaYScaled = "vars/gyro/gyroCameraDelta/y/scaled";
export const touchscreenUserBindings = { export const touchscreenUserBindings = {
[sets.global]: [ [sets.global]: [
...@@ -27,25 +35,48 @@ export const touchscreenUserBindings = { ...@@ -27,25 +35,48 @@ export const touchscreenUserBindings = {
xform: xforms.copy xform: xforms.copy
}, },
{ {
src: { value: paths.device.touchscreen.cameraDelta }, src: { value: paths.device.touchscreen.touchCameraDelta },
dest: { x: "/var/touchscreenCamDeltaX", y: "/var/touchscreenCamDeltaY" }, dest: { x: touchCamDeltaX, y: touchCamDeltaY },
xform: xforms.split_vec2 xform: xforms.split_vec2
}, },
{ {
src: { value: "/var/touchscreenCamDeltaX" }, src: { value: touchCamDeltaX },
dest: { value: "/var/touchscreenCamDeltaXScaled" }, dest: { value: touchCamDeltaXScaled },
xform: xforms.scale(0.18) xform: xforms.scale(0.18)
}, },
{ {
src: { value: "/var/touchscreenCamDeltaY" }, src: { value: touchCamDeltaY },
dest: { value: "/var/touchscreenCamDeltaYScaled" }, dest: { value: touchCamDeltaYScaled },
xform: xforms.scale(0.35) xform: xforms.scale(0.35)
}, },
{ {
src: { x: "/var/touchscreenCamDeltaXScaled", y: "/var/touchscreenCamDeltaYScaled" }, src: { x: touchCamDeltaXScaled, y: touchCamDeltaYScaled },
dest: { value: paths.actions.cameraDelta }, dest: { value: touchCamDelta },
xform: xforms.compose_vec2 xform: xforms.compose_vec2
}, },
{
src: { value: paths.device.gyro.averageDeltaX },
dest: { value: gyroCamDeltaXScaled },
xform: xforms.scale(1.00)
},
{
src: { value: paths.device.gyro.averageDeltaY },
dest: { value: gyroCamDeltaYScaled },
xform: xforms.scale(1.00)
},
{
src: { x: gyroCamDeltaYScaled, y: gyroCamDeltaXScaled },
dest: { value: gyroCamDelta },
xform: xforms.compose_vec2
},
{
src: {
first: touchCamDelta,
second: gyroCamDelta
},
dest: { value: paths.actions.cameraDelta },
xform: xforms.add_vec2
},
{ {
src: { value: paths.device.touchscreen.isTouchingGrabbable }, src: { value: paths.device.touchscreen.isTouchingGrabbable },
dest: { value: paths.actions.cursor.grab }, dest: { value: paths.actions.cursor.grab },
......
...@@ -98,6 +98,10 @@ export const xforms = { ...@@ -98,6 +98,10 @@ export const xforms = {
const second = frame[src.second]; const second = frame[src.second];
if (first && second) { if (first && second) {
frame[dest.value] = [first[0] + second[0], first[1] + second[1]]; frame[dest.value] = [first[0] + second[0], first[1] + second[1]];
} else if (second) {
frame[dest.value] = second;
} else if (first) {
frame[dest.value] = first;
} }
}, },
any: function(frame, src, dest) { any: function(frame, src, dest) {
......
...@@ -94,7 +94,9 @@ export class AppAwareTouchscreenDevice { ...@@ -94,7 +94,9 @@ export class AppAwareTouchscreenDevice {
move(touch) { move(touch) {
if (!touchIsAssigned(touch, this.assignments)) { if (!touchIsAssigned(touch, this.assignments)) {
console.warn("touch does not have job", touch); if (!touch.target.classList[0] || !touch.target.classList[0].startsWith("virtual-gamepad-controls")) {
console.warn("touch does not have job", touch);
}
return; return;
} }
...@@ -232,7 +234,7 @@ export class AppAwareTouchscreenDevice { ...@@ -232,7 +234,7 @@ export class AppAwareTouchscreenDevice {
} }
if (jobIsAssigned(MOVE_CAMERA_JOB, this.assignments)) { if (jobIsAssigned(MOVE_CAMERA_JOB, this.assignments)) {
frame[path.cameraDelta] = findByJob(MOVE_CAMERA_JOB, this.assignments).delta; frame[path.touchCameraDelta] = findByJob(MOVE_CAMERA_JOB, this.assignments).delta;
} }
frame[path.pinch.delta] = this.pinch.delta; frame[path.pinch.delta] = this.pinch.delta;
......
import { paths } from "../paths";
const TWOPI = Math.PI * 2;
class CircularBuffer {
constructor(length) {
this.items = new Array(length).fill(0);
this.writePtr = 0;
}
push(item) {
this.items[this.writePtr] = item;
this.writePtr = (this.writePtr + 1) % this.items.length;
}
}
const abs = Math.abs;
// Input: two numbers between [-Math.PI, Math.PI]
// Output: difference between them, where -Math.PI === Math.PI
const difference = (curr, prev) => {
const a = curr - prev;
const b = curr + TWOPI - prev;
const c = curr - (prev + TWOPI);
if (abs(a) < abs(b)) {
if (abs(a) < abs(c)) {
return a;
}
}
if (abs(b) < abs(c)) {
return b;
}
return c;
};
const average = a => {
let sum = 0;
for (let i = 0; i < a.length; i++) {
const n = a[i];
sum += n;
}
return sum / a.length;
};
export class GyroDevice {
constructor() {
this.hmdEuler = new THREE.Euler();
this.hmdQuaternion = new THREE.Quaternion();
this.prevX = this.hmdEuler.x;
this.prevY = this.hmdEuler.y;
this.dXBuffer = new CircularBuffer(6);
this.dYBuffer = new CircularBuffer(6);
this.vrDisplay = window.webvrpolyfill.getPolyfillDisplays()[0];
this.frameData = new window.webvrpolyfill.constructor.VRFrameData();
}
write(frame) {
const hmdEuler = this.hmdEuler;
this.vrDisplay.getFrameData(this.frameData);
if (this.frameData.pose.orientation !== null) {
this.hmdQuaternion.fromArray(this.frameData.pose.orientation);
hmdEuler.setFromQuaternion(this.hmdQuaternion, "YXZ");
}
const dX = THREE.Math.RAD2DEG * difference(hmdEuler.x, this.prevX);
const dY = THREE.Math.RAD2DEG * difference(hmdEuler.y, this.prevY);
this.dXBuffer.push(Math.abs(dX) < 0.001 ? 0 : dX);
this.dYBuffer.push(Math.abs(dY) < 0.001 ? 0 : dY);
this.averageDeltaX = average(this.dXBuffer.items);
this.averageDeltaY = average(this.dYBuffer.items);
this.prevX = hmdEuler.x;
this.prevY = hmdEuler.y;
frame[paths.device.gyro.averageDeltaX] = this.averageDeltaX;
frame[paths.device.gyro.averageDeltaY] = this.averageDeltaY;
}
}
...@@ -69,12 +69,17 @@ paths.device.smartMouse.cursorPose = "/device/smartMouse/cursorPose"; ...@@ -69,12 +69,17 @@ paths.device.smartMouse.cursorPose = "/device/smartMouse/cursorPose";
paths.device.smartMouse.cameraDelta = "/device/smartMouse/cameraDelta"; paths.device.smartMouse.cameraDelta = "/device/smartMouse/cameraDelta";
paths.device.touchscreen = {}; paths.device.touchscreen = {};
paths.device.touchscreen.cursorPose = "/device/touchscreen/cursorPose"; paths.device.touchscreen.cursorPose = "/device/touchscreen/cursorPose";
paths.device.touchscreen.touchCameraDelta = "/device/touchscreen/touchCameraDelta";
paths.device.touchscreen.gyroCameraDelta = "/device/touchscreen/gyroCameraDelta";
paths.device.touchscreen.cameraDelta = "/device/touchscreen/cameraDelta"; paths.device.touchscreen.cameraDelta = "/device/touchscreen/cameraDelta";
paths.device.touchscreen.pinch = {}; paths.device.touchscreen.pinch = {};
paths.device.touchscreen.pinch.delta = "/device/touchscreen/pinch/delta"; paths.device.touchscreen.pinch.delta = "/device/touchscreen/pinch/delta";
paths.device.touchscreen.pinch.initialDistance = "/device/touchscreen/pinch/initialDistance"; paths.device.touchscreen.pinch.initialDistance = "/device/touchscreen/pinch/initialDistance";
paths.device.touchscreen.pinch.currentDistance = "/device/touchscreen/pinch/currentDistance"; paths.device.touchscreen.pinch.currentDistance = "/device/touchscreen/pinch/currentDistance";
paths.device.touchscreen.isTouchingGrabbable = "/device/touchscreen/isTouchingGrabbable"; paths.device.touchscreen.isTouchingGrabbable = "/device/touchscreen/isTouchingGrabbable";
paths.device.gyro = {};
paths.device.gyro.averageDeltaX = "/device/gyro/averageDeltaX";
paths.device.gyro.averageDeltaY = "/device/gyro/averageDeltaY";
paths.device.hud = {}; paths.device.hud = {};
paths.device.hud.penButton = "/device/hud/penButton"; paths.device.hud.penButton = "/device/hud/penButton";
......
...@@ -9,6 +9,7 @@ import { OculusGoControllerDevice } from "./devices/oculus-go-controller"; ...@@ -9,6 +9,7 @@ import { OculusGoControllerDevice } from "./devices/oculus-go-controller";
import { OculusTouchControllerDevice } from "./devices/oculus-touch-controller"; import { OculusTouchControllerDevice } from "./devices/oculus-touch-controller";
import { DaydreamControllerDevice } from "./devices/daydream-controller"; import { DaydreamControllerDevice } from "./devices/daydream-controller";
import { ViveControllerDevice } from "./devices/vive-controller"; import { ViveControllerDevice } from "./devices/vive-controller";
import { GyroDevice } from "./devices/gyro";
import { AppAwareMouseDevice } from "./devices/app-aware-mouse"; import { AppAwareMouseDevice } from "./devices/app-aware-mouse";
import { AppAwareTouchscreenDevice } from "./devices/app-aware-touchscreen"; import { AppAwareTouchscreenDevice } from "./devices/app-aware-touchscreen";
...@@ -101,14 +102,17 @@ AFRAME.registerSystem("userinput", { ...@@ -101,14 +102,17 @@ AFRAME.registerSystem("userinput", {
this.gamepads = []; this.gamepads = [];
const appAwareTouchscreenDevice = new AppAwareTouchscreenDevice(); const appAwareTouchscreenDevice = new AppAwareTouchscreenDevice();
const gyroDevice = new GyroDevice();
const updateBindingsForVRMode = () => { const updateBindingsForVRMode = () => {
const inVRMode = this.el.sceneEl.is("vr-mode"); const inVRMode = this.el.sceneEl.is("vr-mode");
if (AFRAME.utils.device.isMobile()) { if (AFRAME.utils.device.isMobile()) {
if (inVRMode) { if (inVRMode) {
this.activeDevices.delete(appAwareTouchscreenDevice); this.activeDevices.delete(appAwareTouchscreenDevice);
this.activeDevices.delete(gyroDevice);
this.registeredMappings.delete(touchscreenUserBindings); this.registeredMappings.delete(touchscreenUserBindings);
} else { } else {
this.activeDevices.add(appAwareTouchscreenDevice); this.activeDevices.add(appAwareTouchscreenDevice);
this.activeDevices.add(gyroDevice);
this.registeredMappings.add(touchscreenUserBindings); this.registeredMappings.add(touchscreenUserBindings);
} }
} else { } else {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment