Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Examples/Geometry/VR/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const renderWindow = fullScreenRenderer.getRenderWindow();
// this
// ----------------------------------------------------------------------------

const coneSource = vtkConeSource.newInstance({ height: 100.0, radius: 50 });
const coneSource = vtkConeSource.newInstance({ height: 1.0, radius: 0.5 });
const filter = vtkCalculator.newInstance();

filter.setInputConnection(coneSource.getOutputPort());
Expand Down Expand Up @@ -66,7 +66,7 @@ mapper.setInputConnection(filter.getOutputPort());

const actor = vtkActor.newInstance();
actor.setMapper(mapper);
actor.setPosition(0.0, 0.0, -20.0);
actor.setPosition(0.0, 0.0, -1.0);

renderer.addActor(actor);
renderer.resetCamera();
Expand Down
40 changes: 34 additions & 6 deletions Sources/Interaction/Style/InteractorStyleTrackballCamera/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import vtkInteractorStyle from 'vtk.js/Sources/Rendering/Core/InteractorStyle';
import vtkInteractorStyleConstants from 'vtk.js/Sources/Rendering/Core/InteractorStyle/Constants';
import * as vtkMath from 'vtk.js/Sources/Common/Core/Math';
import {
Axis,
Device,
Input,
} from 'vtk.js/Sources/Rendering/Core/RenderWindowInteractor/Constants';
Expand Down Expand Up @@ -58,7 +59,7 @@ function vtkInteractorStyleTrackballCamera(publicAPI, model) {
ed &&
ed.pressed &&
ed.device === Device.RightController &&
ed.input === Input.TrackPad
ed.input === Input.Trigger // || ed.input === Input.TrackPad)
) {
publicAPI.startCameraPose();
return;
Expand All @@ -67,37 +68,64 @@ function vtkInteractorStyleTrackballCamera(publicAPI, model) {
ed &&
!ed.pressed &&
ed.device === Device.RightController &&
ed.input === Input.TrackPad &&
ed.input === Input.Trigger && // || ed.input === Input.TrackPad) &&
model.state === States.IS_CAMERA_POSE
) {
publicAPI.endCameraPose();
// return;
}
if (
ed &&
ed.device === Device.RightController &&
ed.input &&
(ed.input === Axis.ThumbstickX || Input.TrackPad)
) {
publicAPI.updateCameraOrientation(ed);
}
if (ed && ed.input && (ed.input === Axis.ThumbstickX || Input.TrackPad)) {
publicAPI.updateCameraOrientation(ed);
}
};

publicAPI.handleMove3D = (ed) => {
switch (model.state) {
case States.IS_CAMERA_POSE:
publicAPI.updateCameraPose(ed);
publicAPI.updateCameraPosition(ed);
break;
default:
}
};

publicAPI.updateCameraPose = (ed) => {
publicAPI.updateCameraOrientation = (ed) => {
// rotate the world in the direction
// of the controller
const camera = ed.pokedRenderer.getActiveCamera();
let worldMatrix = new Float64Array(16);
camera.getWorldToPhysicalMatrix(worldMatrix);

const angle = ed.device === Device.LeftController ? -22.5 : 22.5;
camera.applyPhysicalYaw(angle);
};

publicAPI.updateCameraPosition = (ed) => {
// move the world in the direction of the
// controller
const camera = ed.pokedRenderer.getActiveCamera();
const oldTrans = camera.getPhysicalTranslation();

// look at the y axis to determine how fast / what direction to move
const speed = ed.gamepad.axes[1];
const speed = 0.5; // ed.gamepad.axes[1];

// 0.05 meters / frame movement
const pscale = speed * 0.05 * camera.getPhysicalScale();

// convert orientation to world coordinate direction
const dir = camera.physicalOrientationToWorldDirection(ed.orientation);
const dir = camera.physicalOrientationToWorldDirection([
ed.orientation.x,
ed.orientation.y,
ed.orientation.z,
ed.orientation.w,
]);

camera.setPhysicalTranslation(
oldTrans[0] + dir[0] * pscale,
Expand Down
59 changes: 58 additions & 1 deletion Sources/Rendering/Core/Camera/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -399,9 +399,43 @@ function vtkCamera(publicAPI, model) {
mat4.translate(result, result, model.physicalTranslation);
};

publicAPI.applyPhysicalYaw = (angle) => {
model.physicalYawAngle += angle;
if (model.physicalYawAngle > 360) {
model.physicalYawAngle -= 360;
} else if (model.physicalYawAngle < 0) {
model.physicalYawAngle += 360;
}
};

publicAPI.computeViewParametersFromViewMatrix = (vmat) => {
// invert to get view to world
mat4.invert(tmpMatrix, vmat);
let viewToWorldPos = new Float64Array(4);
let v2wMatrix = new Float64Array(16);
mat4.invert(v2wMatrix, vmat);

viewToWorldPos[0] = v2wMatrix[12];
viewToWorldPos[1] = v2wMatrix[13];
viewToWorldPos[2] = v2wMatrix[14];
viewToWorldPos[3] = 1;

mat4.copy(tmpMatrix, v2wMatrix);

let rotate = mat4.identity(new Float64Array(16));
let v2wTranslate = mat4.identity(new Float64Array(16));
let v2wTranslateReverse = mat4.identity(new Float64Array(16));

mat4.translate(v2wTranslate, v2wTranslate, [
-viewToWorldPos[0],
-viewToWorldPos[1],
-viewToWorldPos[2],
]);
mat4.rotateY(rotate, rotate, (model.physicalYawAngle / 180) * 3.14);
mat4.translate(v2wTranslateReverse, v2wTranslateReverse, viewToWorldPos);

mat4.mul(tmpMatrix, v2wTranslate, v2wMatrix);
mat4.mul(tmpMatrix, tmpMatrix, rotate);
mat4.mul(tmpMatrix, v2wTranslateReverse, tmpMatrix);

// note with glmatrix operations happen in
// the reverse order
Expand Down Expand Up @@ -443,6 +477,27 @@ function vtkCamera(publicAPI, model) {
// world -> view
mat4.multiply(tmpMatrix, mat, tmpMatrix);

console.log('orig');
console.log(tmpMatrix);
let rot = mat4.identity(new Float64Array(16));
let v = new Float64Array(3);
v[0] = tmpMatrix[12];
v[1] = tmpMatrix[13];
v[2] = tmpMatrix[14];
//mat4.rotateY(rot, rot, model.physicalYawAngle / 180 * 3.14);
// mat4.transpose(rot, rot);
//mat4.translate(tmpMatrix, tmpMatrix, [-v[0], -v[1], -v[2]]);
mat4.mul(tmpMatrix, tmpMatrix, rot);
tmpMatrix[12] = v[0];
tmpMatrix[13] = v[1];
tmpMatrix[14] = v[2];
//mat4.mul(tmpMatrix, rot, tmpMatrix);
//mat4.translate(tmpMatrix, tmpMatrix, v);

console.log(tmpMatrix);
let tmp = tmpMatrix[12];
//tmpMatrix[12] = tmpMatrix[14];
//tmpMatrix[14] = tmp;
publicAPI.computeViewParametersFromViewMatrix(tmpMatrix);
};

Expand Down Expand Up @@ -721,6 +776,8 @@ export const DEFAULT_VALUES = {
physicalScale: 1.0,
physicalViewUp: [0, 1, 0],
physicalViewNorth: [0, 0, -1],

physicalYawAngle: 0,
};

// ----------------------------------------------------------------------------
Expand Down
14 changes: 13 additions & 1 deletion Sources/Rendering/Core/RenderWindowInteractor/Constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,22 @@ export const Input = {
Trigger: 1,
TrackPad: 2,
Grip: 3,
ApplicationMenu: 4,
Thumbstick: 4,
A: 5,
B: 6,
ApplicationMenu: 7, // Not exposed in WebXR API
};

export const Axis = {
Unknown: 0,
TouchpadX: 1,
TouchpadY: 2,
ThumbstickX: 3,
ThumbstickY: 4,
};

export default {
Device,
Input,
Axis,
};
161 changes: 109 additions & 52 deletions Sources/Rendering/Core/RenderWindowInteractor/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as vtkMath from 'vtk.js/Sources/Common/Core/Math';

import Constants from 'vtk.js/Sources/Rendering/Core/RenderWindowInteractor/Constants';

const { Device, Input } = Constants;
const { Axis, Device, Input } = Constants;
const { vtkWarningMacro, vtkErrorMacro, normalizeWheel, vtkOnceErrorMacro } =
macro;

Expand All @@ -12,12 +12,17 @@ const { vtkWarningMacro, vtkErrorMacro, normalizeWheel, vtkOnceErrorMacro } =
// ----------------------------------------------------------------------------

const deviceInputMap = {
'OpenVR Gamepad': [
Input.TrackPad,
Input.Trigger,
Input.Grip,
Input.ApplicationMenu,
],
'xr-standard': {
button: [
Input.Trigger,
Input.Grip,
Input.TrackPad,
Input.Thumbstick,
Input.A,
Input.B,
],
axis: [Axis.TouchpadX, Axis.TouchpadY, Axis.ThumbstickX, Axis.ThumbstickY],
},
};

const handledEvents = [
Expand Down Expand Up @@ -415,55 +420,107 @@ function vtkRenderWindowInteractor(publicAPI, model) {
}
};

publicAPI.updateGamepads = (displayId) => {
const gamepads = navigator.getGamepads();

publicAPI.updateXRGamepads = (xrSession, xrFrame, xrRefSpace) => {
// Fire binary events when axis magnitude crosses threshold
const axisThreshold = 0.9;
// watch for when buttons change state and fire events
for (let i = 0; i < gamepads.length; ++i) {
const gp = gamepads[i];
if (gp && gp.displayId === displayId) {
if (!(gp.index in model.lastGamepadValues)) {
model.lastGamepadValues[gp.index] = { buttons: {} };
xrSession.inputSources.forEach((inputSource) => {
const gp = inputSource.gamepad;
if (gp === null) return;

const pose = xrFrame.getPose(inputSource.gripSpace, xrRefSpace);
const hand = inputSource.handedness;

// Init
if (!(gp.index in model.lastGamepadValues)) {
model.lastGamepadValues[gp.index] = {
left: {
buttons: {},
axes: {},
},
right: {
buttons: {},
axes: {},
},
};
}

// Query buttons
for (let b = 0; b < gp.buttons.length; ++b) {
// Init
if (!(b in model.lastGamepadValues[gp.index][hand].buttons)) {
model.lastGamepadValues[gp.index][hand].buttons[b] = false;
}

// State change
if (
model.lastGamepadValues[gp.index][hand].buttons[b] !==
gp.buttons[b].pressed
) {
publicAPI.button3DEvent({
gamepad: gp,
position: pose.transform.position,
orientation: pose.transform.orientation,
pressed: gp.buttons[b].pressed,
device:
hand === 'left' ? Device.LeftController : Device.RightController,
input:
deviceInputMap[gp.mapping] &&
deviceInputMap[gp.mapping]['button'][b]
? deviceInputMap[gp.mapping]['button'][b]
: Input.Trigger,
});
model.lastGamepadValues[gp.index][hand].buttons[b] =
gp.buttons[b].pressed;
}
for (let b = 0; b < gp.buttons.length; ++b) {
if (!(b in model.lastGamepadValues[gp.index].buttons)) {
model.lastGamepadValues[gp.index].buttons[b] = false;
}
if (
model.lastGamepadValues[gp.index].buttons[b] !==
gp.buttons[b].pressed
) {
publicAPI.button3DEvent({
gamepad: gp,
position: gp.pose.position,
orientation: gp.pose.orientation,
pressed: gp.buttons[b].pressed,
device:
gp.hand === 'left'
? Device.LeftController
: Device.RightController,
input:
deviceInputMap[gp.id] && deviceInputMap[gp.id][b]
? deviceInputMap[gp.id][b]
: Input.Trigger,
});
model.lastGamepadValues[gp.index].buttons[b] =
gp.buttons[b].pressed;
}
if (model.lastGamepadValues[gp.index].buttons[b]) {
publicAPI.move3DEvent({
gamepad: gp,
position: gp.pose.position,
orientation: gp.pose.orientation,
device:
gp.hand === 'left'
? Device.LeftController
: Device.RightController,
});
}

// State
if (gp.buttons[b].pressed) {
publicAPI.move3DEvent({
gamepad: gp,
position: pose.transform.position,
orientation: pose.transform.orientation,
device:
hand === 'left' ? Device.LeftController : Device.RightController,
input:
deviceInputMap[gp.mapping] &&
deviceInputMap[gp.mapping]['button'][b]
? deviceInputMap[gp.mapping]['button'][b]
: Input.Unknown,
});
}
}
}

for (let a = 0; a < gp.axes.length; ++a) {
// Init
if (!(a in model.lastGamepadValues[gp.index][hand].axes)) {
model.lastGamepadValues[gp.index][hand].axes[a] = 0.0;
}

// State change
if (
gp.axes[a] > axisThreshold &&
model.lastGamepadValues[gp.index][hand].axes[a] < axisThreshold
) {
}

// State
// TODO debounce
if (gp.axes[a] > axisThreshold) {
//model.rotate3DEvent({
// gamepad: gp,
// position: pose.transform.position,
// orientation: pose.transform.orientation,
// device:
// hand === 'left' ? Device.LeftController : Device.RightController,
// input: deviceInputMap[gp.mapping] && deviceInputMap[gp.mapping]['axis'][a]
// ? deviceInputMap[gp.mapping]['axis'][a]
// : Axis.Unknown,
// value: gp.axes[a],
//});
}
}
});
};

publicAPI.handleMouseMove = (event) => {
Expand Down
Loading