@kitware/vtk.js 30.0.0 → 30.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,182 @@
1
+ import { vec3, vec4, quat, mat4 } from 'gl-matrix';
2
+ import vtkCompositeVRManipulator from '../CompositeVRManipulator.js';
3
+ import vtkPicker from '../../../Rendering/Core/Picker.js';
4
+ import { m as macro } from '../../../macros2.js';
5
+ import { States } from '../../../Rendering/Core/InteractorStyle/Constants.js';
6
+
7
+ // ----------------------------------------------------------------------------
8
+ // vtk3DControllerModelSelectorManipulator methods
9
+ // ----------------------------------------------------------------------------
10
+
11
+ function vtk3DControllerModelSelectorManipulator(publicAPI, model) {
12
+ model.classHierarchy.push('vtk3DControllerModelSelectorManipulator');
13
+ const picker = vtkPicker.newInstance();
14
+
15
+ // The current prop we are manipulating, if any.
16
+ let pickedProp;
17
+
18
+ // pre-allocate array buffers
19
+ const physicalToWorldMatrix = new Float64Array(16);
20
+
21
+ // arrays holding deltas from lastWorldPosition and lastOrientation
22
+ const translation = new Float64Array(3);
23
+ const rotation = new Float64Array(4);
24
+ const lastOrientationConjugate = new Float64Array(4);
25
+ const orientationAxis = new Float64Array(3);
26
+
27
+ // arrays holding the transform
28
+ const computedTransform = new Float64Array(16);
29
+ const computedTransformRotation = new Float64Array(4);
30
+
31
+ // arrays holding the current state of pickedProp.
32
+ const transposedPropMatrix = new Float64Array(16);
33
+ const propCurrentOrientation = new Float64Array(4);
34
+ const propCurrentOrientationConjugate = new Float64Array(4);
35
+
36
+ // arrays holding the new properties that must be assigned to pickedProp.
37
+ const propNewTranslation = new Float64Array(3);
38
+ const propNewScaling = new Float64Array(3);
39
+ const propNewOrientation = new Float64Array(4);
40
+ function applyPositionAndOrientationToProp(prop, worldPosition, orientation) {
41
+ vec3.subtract(translation, worldPosition, model.lastWorldPosition);
42
+ quat.conjugate(lastOrientationConjugate, model.lastOrientation);
43
+ quat.multiply(rotation, orientation, lastOrientationConjugate);
44
+ quat.normalize(rotation, rotation);
45
+ const rotationAngle = quat.getAxisAngle(orientationAxis, rotation);
46
+
47
+ // reset to identity
48
+ mat4.identity(computedTransform);
49
+
50
+ // compute transform
51
+ mat4.translate(computedTransform, computedTransform, worldPosition);
52
+ mat4.rotate(computedTransform, computedTransform, rotationAngle, orientationAxis);
53
+ mat4.translate(computedTransform, computedTransform, vec3.negate(new Float64Array(3), worldPosition));
54
+ mat4.translate(computedTransform, computedTransform, translation);
55
+
56
+ // lookup the prop internal matrix
57
+ mat4.transpose(transposedPropMatrix, prop.getMatrix());
58
+ // apply the new computedTransform to the prop internal matrix
59
+ mat4.multiply(computedTransform, computedTransform, transposedPropMatrix);
60
+
61
+ // Multiply the computedTransform with the current prop orientation to get the delta
62
+ // that must be applied to the prop
63
+ mat4.getRotation(computedTransformRotation, computedTransform);
64
+ prop.getOrientationQuaternion(propCurrentOrientation);
65
+ quat.conjugate(propCurrentOrientationConjugate, propCurrentOrientation);
66
+ quat.multiply(propNewOrientation, propCurrentOrientationConjugate, computedTransformRotation);
67
+ quat.normalize(propNewOrientation, propNewOrientation);
68
+ mat4.getTranslation(propNewTranslation, computedTransform);
69
+ mat4.getScaling(propNewScaling, computedTransform);
70
+
71
+ // Update the prop internal matrix
72
+ prop.setPosition(...propNewTranslation);
73
+ prop.setScale(...propNewScaling);
74
+ prop.rotateQuaternion(propNewOrientation);
75
+ }
76
+ function releasePickedProp() {
77
+ model.lastOrientation = null;
78
+ model.lastWorldPosition = null;
79
+ if (pickedProp) {
80
+ pickedProp.setDragable(true);
81
+ }
82
+ pickedProp = null;
83
+ }
84
+ publicAPI.onButton3D = (interactorStyle, renderer, state, eventData) => {
85
+ // If the button is not pressed, clear the state
86
+ if (!eventData.pressed) {
87
+ releasePickedProp();
88
+ return macro.VOID;
89
+ }
90
+ const camera = renderer.getActiveCamera();
91
+ camera.getPhysicalToWorldMatrix(physicalToWorldMatrix);
92
+ const {
93
+ targetPosition,
94
+ targetOrientation
95
+ } = eventData;
96
+
97
+ // Since targetPosition is in physical coordinates,
98
+ // transform it using the physicalToWorldMatrix to get it in world coordinates
99
+ const targetRayWorldPosition = vec3.transformMat4([], [targetPosition.x, targetPosition.y, targetPosition.z], physicalToWorldMatrix);
100
+ const targetRayWorldDirection = camera.physicalOrientationToWorldDirection([targetOrientation.x, targetOrientation.y, targetOrientation.z, targetOrientation.w]);
101
+ const dist = renderer.getActiveCamera().getClippingRange()[1];
102
+ const rayPoint1 = [...targetRayWorldPosition, 1.0];
103
+ const rayPoint2 = [rayPoint1[0] - targetRayWorldDirection[0] * dist, rayPoint1[1] - targetRayWorldDirection[1] * dist, rayPoint1[2] - targetRayWorldDirection[2] * dist, 1.0];
104
+
105
+ // Perform picking on the given renderer
106
+ picker.pick3DPoint(rayPoint1, rayPoint2, renderer);
107
+ const props = picker.getActors();
108
+
109
+ // If we have picked props, store the first one.
110
+ if (props.length > 0 && props[0].getNestedDragable()) {
111
+ pickedProp = props[0];
112
+
113
+ // prevent the prop from being dragged somewhere else
114
+ pickedProp.setDragable(false);
115
+ } else {
116
+ releasePickedProp();
117
+ }
118
+ return macro.EVENT_ABORT;
119
+ };
120
+
121
+ // pre-allocation to reduce gc in onMove3D
122
+ const currentTargetRayWorldPosition = new Float64Array(3);
123
+ const currentTargetRayOrientation = new Float64Array(4);
124
+ publicAPI.onMove3D = (interactorStyle, renderer, state, eventData) => {
125
+ // If we are not interacting with any prop, we have nothing to do.
126
+ // Also check for dragable
127
+ if (state !== States.IS_CAMERA_POSE || pickedProp == null) {
128
+ return macro.VOID;
129
+ }
130
+ const camera = renderer.getActiveCamera();
131
+ camera.getPhysicalToWorldMatrix(physicalToWorldMatrix);
132
+ const {
133
+ targetPosition
134
+ } = eventData;
135
+ vec3.transformMat4(currentTargetRayWorldPosition, [targetPosition.x, targetPosition.y, targetPosition.z], physicalToWorldMatrix);
136
+
137
+ // this is a unit quaternion
138
+ vec4.set(currentTargetRayOrientation, eventData.targetOrientation.x, eventData.targetOrientation.y, eventData.targetOrientation.z, eventData.targetOrientation.w);
139
+ if (model.lastWorldPosition && model.lastOrientation) {
140
+ applyPositionAndOrientationToProp(pickedProp, currentTargetRayWorldPosition, currentTargetRayOrientation);
141
+ } else {
142
+ // allocate
143
+ model.lastWorldPosition = new Float64Array(3);
144
+ model.lastOrientation = new Float64Array(4);
145
+ }
146
+ vec3.copy(model.lastWorldPosition, currentTargetRayWorldPosition);
147
+ vec4.copy(model.lastOrientation, currentTargetRayOrientation);
148
+ return macro.EVENT_ABORT;
149
+ };
150
+ }
151
+
152
+ // ----------------------------------------------------------------------------
153
+ // Object factory
154
+ // ----------------------------------------------------------------------------
155
+
156
+ const DEFAULT_VALUES = {};
157
+
158
+ // ----------------------------------------------------------------------------
159
+
160
+ function extend(publicAPI, model) {
161
+ let initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
162
+ Object.assign(model, DEFAULT_VALUES, initialValues);
163
+ macro.get(publicAPI, model, ['lastWorldPosition', 'lastOrientation']);
164
+ macro.obj(publicAPI, model);
165
+ vtkCompositeVRManipulator.extend(publicAPI, model, initialValues);
166
+
167
+ // Object specific methods
168
+ vtk3DControllerModelSelectorManipulator(publicAPI, model);
169
+ }
170
+
171
+ // ----------------------------------------------------------------------------
172
+
173
+ const newInstance = macro.newInstance(extend, 'vtk3DControllerModelSelectorManipulator');
174
+
175
+ // ----------------------------------------------------------------------------
176
+
177
+ var vtk3DControllerModelSelectorManipulator$1 = {
178
+ newInstance,
179
+ extend
180
+ };
181
+
182
+ export { vtk3DControllerModelSelectorManipulator$1 as default, extend, newInstance };
@@ -0,0 +1,19 @@
1
+ import vtkCompositeVRManipulator from './CompositeVRManipulator';
2
+
3
+ export interface vtk3DControllerModelSelectorManipulator
4
+ extends vtkCompositeVRManipulator {}
5
+
6
+ export interface I3DControllerModelSelectorManipulatorInitialValues
7
+ extends vtkCompositeVRManipulator {}
8
+
9
+ export function extend(
10
+ publicAPI: object,
11
+ model: object,
12
+ initialValues?: I3DControllerModelSelectorManipulatorInitialValues
13
+ ): void;
14
+
15
+ export const vtk3DControllerModelSelectorManipulator: {
16
+ extend: typeof extend;
17
+ };
18
+
19
+ export default vtk3DControllerModelSelectorManipulator;
@@ -1,28 +1,28 @@
1
1
  import { States } from './../../Rendering/Core/InteractorStyle/Constants';
2
2
  import vtkRenderer from './../../Rendering/Core/Renderer';
3
- import vtkRenderWindowInteractor from './../../Rendering/Core/RenderWindowInteractor';
3
+ import vtkInteractorObserver from './../../Rendering/Core/InteractorObserver';
4
4
  import {
5
5
  Device,
6
6
  Input,
7
7
  } from './../../Rendering/Core/RenderWindowInteractor/Constants';
8
+ import {
9
+ I3DEvent,
10
+ IButton3DEvent,
11
+ } from './../../Rendering/Core/RenderWindowInteractor';
8
12
 
9
13
  export interface vtkCompositeVRManipulator {
10
14
  onButton3D(
11
- interactor: vtkRenderWindowInteractor,
15
+ interactorStyle: vtkInteractorObserver,
12
16
  renderer: vtkRenderer,
13
17
  state: States,
14
- device: Device,
15
- input: Input,
16
- pressed: boolean
18
+ eventData: IButton3DEvent
17
19
  ): void;
18
20
 
19
21
  onMove3D(
20
- interactor: vtkRenderWindowInteractor,
22
+ interactorStyle: vtkInteractorObserver,
21
23
  renderer: vtkRenderer,
22
24
  state: States,
23
- device: Device,
24
- input: Input,
25
- pressed: boolean
25
+ eventData: I3DEvent
26
26
  ): void;
27
27
  }
28
28
 
@@ -8,8 +8,8 @@ import { Device, Input } from '../../Rendering/Core/RenderWindowInteractor/Const
8
8
  function vtkCompositeVRManipulator(publicAPI, model) {
9
9
  // Set our className
10
10
  model.classHierarchy.push('vtkCompositeVRManipulator');
11
- publicAPI.onButton3D = (interactor, renderer, state, device, input, pressed) => {};
12
- publicAPI.onMove3D = (interactor, renderer, state, device, input, pressed) => {};
11
+ publicAPI.onButton3D = (interactorStyle, renderer, state, eventData) => {};
12
+ publicAPI.onMove3D = (interactorStyle, renderer, state, eventData) => {};
13
13
  }
14
14
 
15
15
  // ----------------------------------------------------------------------------
@@ -10,14 +10,14 @@ import { States } from '../../Rendering/Core/InteractorStyle/Constants.js';
10
10
  function vtkVRButtonPanManipulator(publicAPI, model) {
11
11
  // Set our className
12
12
  model.classHierarchy.push('vtkVRButtonPanManipulator');
13
- publicAPI.onButton3D = (interactorStyle, renderer, state, device, input, pressed) => {
14
- if (pressed) {
13
+ publicAPI.onButton3D = (interactorStyle, renderer, state, eventData) => {
14
+ if (eventData.pressed) {
15
15
  interactorStyle.startCameraPose();
16
16
  } else if (state === States.IS_CAMERA_POSE) {
17
17
  interactorStyle.endCameraPose();
18
18
  }
19
19
  };
20
- publicAPI.onMove3D = (interactorStyle, renderer, state, data) => {
20
+ publicAPI.onMove3D = (interactorStyle, renderer, state, eventData) => {
21
21
  if (state !== States.IS_CAMERA_POSE) {
22
22
  return;
23
23
  }
@@ -28,13 +28,13 @@ function vtkVRButtonPanManipulator(publicAPI, model) {
28
28
  const oldTrans = camera.getPhysicalTranslation();
29
29
 
30
30
  // look at the y axis to determine how fast / what direction to move
31
- const speed = data.gamepad.axes[1];
31
+ const speed = eventData.gamepad.axes[1];
32
32
 
33
33
  // 0.05 meters / frame movement
34
34
  const pscale = speed * 0.05 * camera.getPhysicalScale();
35
35
 
36
36
  // convert orientation to world coordinate direction
37
- const dir = camera.physicalOrientationToWorldDirection(data.orientation);
37
+ const dir = camera.physicalOrientationToWorldDirection(eventData.orientation);
38
38
  camera.setPhysicalTranslation(oldTrans[0] + dir[0] * pscale, oldTrans[1] + dir[1] * pscale, oldTrans[2] + dir[2] * pscale);
39
39
  };
40
40
  }
@@ -0,0 +1,22 @@
1
+ import vtkInteractorStyleManipulator, { IInteractorStyleManipulatorInitialValues } from './InteractorStyleManipulator';
2
+
3
+ export interface vtkInteractorStyleHMDXR extends vtkInteractorStyleManipulator {}
4
+
5
+ export interface IInteractorStyleHMDXRInitialValues extends IInteractorStyleManipulatorInitialValues {}
6
+
7
+ export function newInstance(
8
+ initialValues?: IInteractorStyleHMDXRInitialValues
9
+ ): vtkInteractorStyleHMDXR;
10
+
11
+ export function extend(
12
+ publicAPI: object,
13
+ model: object,
14
+ initialValues?: IInteractorStyleHMDXRInitialValues
15
+ ): void;
16
+
17
+ export const vtkInteractorStyleHMDXR: {
18
+ newInstance: typeof newInstance;
19
+ extend: typeof extend;
20
+ };
21
+
22
+ export default vtkInteractorStyleHMDXR;
@@ -0,0 +1,50 @@
1
+ import { m as macro } from '../../macros2.js';
2
+ import vtkInteractorStyleManipulator from './InteractorStyleManipulator.js';
3
+ import vtk3DControllerModelSelectorManipulator from '../Manipulators/3DControllerModelSelectorManipulator/index.js';
4
+ import { Device, Input } from '../../Rendering/Core/RenderWindowInteractor/Constants.js';
5
+
6
+ function vtkInteractorStyleHMDXR(publicAPI, model) {
7
+ model.classHierarchy.push('vtkInteractorStyleHMDXR');
8
+ const leftHandManipulator = vtk3DControllerModelSelectorManipulator.newInstance({
9
+ device: Device.LeftController,
10
+ input: Input.A
11
+ });
12
+ const rightHandManipulator = vtk3DControllerModelSelectorManipulator.newInstance({
13
+ device: Device.RightController,
14
+ input: Input.A
15
+ });
16
+ publicAPI.addVRManipulator(leftHandManipulator);
17
+ publicAPI.addVRManipulator(rightHandManipulator);
18
+ }
19
+
20
+ // ----------------------------------------------------------------------------
21
+ // Object factory
22
+ // ----------------------------------------------------------------------------
23
+
24
+ const DEFAULT_VALUES = {};
25
+
26
+ // ----------------------------------------------------------------------------
27
+
28
+ function extend(publicAPI, model) {
29
+ let initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
30
+ Object.assign(model, DEFAULT_VALUES, initialValues);
31
+
32
+ // Inheritance
33
+ vtkInteractorStyleManipulator.extend(publicAPI, model, initialValues);
34
+
35
+ // Object specific methods
36
+ vtkInteractorStyleHMDXR(publicAPI, model);
37
+ }
38
+
39
+ // ----------------------------------------------------------------------------
40
+
41
+ const newInstance = macro.newInstance(extend, 'vtkInteractorStyleHMDXR');
42
+
43
+ // ----------------------------------------------------------------------------
44
+
45
+ var index = {
46
+ newInstance,
47
+ extend
48
+ };
49
+
50
+ export { index as default, extend, newInstance };
@@ -118,6 +118,7 @@ const STATIC = {
118
118
  function vtkInteractorStyleManipulator(publicAPI, model) {
119
119
  // Set our className
120
120
  model.classHierarchy.push('vtkInteractorStyleManipulator');
121
+ model.currentVRManipulators = new Map();
121
122
  model.mouseManipulators = [];
122
123
  model.keyboardManipulators = [];
123
124
  model.vrManipulators = [];
@@ -246,13 +247,20 @@ function vtkInteractorStyleManipulator(publicAPI, model) {
246
247
  }
247
248
 
248
249
  // Look for a matching 3D camera interactor.
249
- model.currentManipulator = publicAPI.findVRManipulator(ed.device, ed.input, ed.pressed);
250
- if (model.currentManipulator) {
251
- model.currentManipulator.onButton3D(publicAPI, ed.pokedRenderer, model.state, ed.device, ed.input, ed.pressed);
250
+ const manipulator = publicAPI.findVRManipulator(ed.device, ed.input, ed.pressed);
251
+ if (manipulator) {
252
+ // register the manipulator for this device
253
+ model.currentVRManipulators.set(ed.device, manipulator);
254
+ manipulator.onButton3D(publicAPI, ed.pokedRenderer, model.state, ed);
252
255
  if (ed.pressed) {
253
256
  publicAPI.startCameraPose();
254
257
  } else {
255
- publicAPI.endCameraPose();
258
+ model.currentVRManipulators.delete(ed.device);
259
+
260
+ // make sure we don't end camera pose if other VR manipulators are currently interacting
261
+ if (model.currentVRManipulators.size === 0) {
262
+ publicAPI.endCameraPose();
263
+ }
256
264
  }
257
265
  } else {
258
266
  vtkDebugMacro('No manipulator found');
@@ -261,8 +269,9 @@ function vtkInteractorStyleManipulator(publicAPI, model) {
261
269
 
262
270
  //-------------------------------------------------------------------------
263
271
  publicAPI.handleMove3D = ed => {
264
- if (model.currentManipulator && model.state === States.IS_CAMERA_POSE) {
265
- model.currentManipulator.onMove3D(publicAPI, ed.pokedRenderer, model.state, ed);
272
+ const manipulator = model.currentVRManipulators.get(ed.device);
273
+ if (manipulator && model.state === States.IS_CAMERA_POSE) {
274
+ manipulator.onMove3D(publicAPI, ed.pokedRenderer, model.state, ed);
266
275
  }
267
276
  };
268
277
 
@@ -576,7 +585,7 @@ function vtkInteractorStyleManipulator(publicAPI, model) {
576
585
  // Object factory
577
586
  // ----------------------------------------------------------------------------
578
587
 
579
- const DEFAULT_VALUES = {
588
+ const defaultValues = initialValues => ({
580
589
  cachedMousePosition: null,
581
590
  currentManipulator: null,
582
591
  currentWheelManipulator: null,
@@ -585,14 +594,15 @@ const DEFAULT_VALUES = {
585
594
  // vrManipulators: null,
586
595
  // gestureManipulators: null,
587
596
  centerOfRotation: [0, 0, 0],
588
- rotationFactor: 1
589
- };
597
+ rotationFactor: 1,
598
+ ...initialValues
599
+ });
590
600
 
591
601
  // ----------------------------------------------------------------------------
592
602
 
593
603
  function extend(publicAPI, model) {
594
604
  let initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
595
- Object.assign(model, DEFAULT_VALUES, initialValues);
605
+ Object.assign(model, defaultValues(initialValues));
596
606
 
597
607
  // Inheritance
598
608
  vtkInteractorStyle.extend(publicAPI, model, initialValues);
@@ -1,4 +1,4 @@
1
- import { mat4 } from "gl-matrix";
1
+ import { mat4, quat } from "gl-matrix";
2
2
  import { Bounds, Vector3, Range } from './../../types';
3
3
  import vtkProp, { IPropInitialValues } from './Prop';
4
4
 
@@ -88,6 +88,13 @@ export interface vtkProp3D extends vtkProp {
88
88
  */
89
89
  getOrientationWXYZ(): number[];
90
90
 
91
+ /**
92
+ * Get the orientation quaternion of the Prop3D.
93
+ * out is optional and will be created if not supplied.
94
+ * @param {quat | undefined} out
95
+ */
96
+ getOrientationQuaternion(out?: quat): quat;
97
+
91
98
  /**
92
99
  * Get a reference to the Prop3D’s 4x4 composite matrix.
93
100
  * Get the matrix from the position, origin, scale and orientation This
@@ -167,6 +174,14 @@ export interface vtkProp3D extends vtkProp {
167
174
  */
168
175
  rotateWXYZ(degrees: number, x: number, y: number, z: number): void;
169
176
 
177
+ /**
178
+ * Rotate the Prop3D by the provided orientation quaternion.
179
+ * If the provided quaternion is identity (~epsilon), this function does nothing.
180
+ * The quaternion should follow the gl-matrix convention: [x,y,z,w]
181
+ * @param {quat} orientationQuaternion The quaternion to rotate the prop by.
182
+ */
183
+ rotateQuaternion(orientationQuaternion: quat): void;
184
+
170
185
  /**
171
186
  * Orientation is specified as X, Y and Z rotations in that order,
172
187
  * but they are performed as RotateZ, RotateX, and finally RotateY.
@@ -4,6 +4,8 @@ import vtkBoundingBox from '../../Common/DataModel/BoundingBox.js';
4
4
  import { A as degreesFromRadians, r as radiansFromDegrees, a as areMatricesEqual } from '../../Common/Core/Math/index.js';
5
5
  import vtkProp from './Prop.js';
6
6
 
7
+ const VTK_EPSILON = 1e-6;
8
+
7
9
  // ----------------------------------------------------------------------------
8
10
  // vtkProp3D methods
9
11
  // ----------------------------------------------------------------------------
@@ -22,6 +24,10 @@ function vtkProp3D(publicAPI, model) {
22
24
  const w = quat.getAxisAngle(oaxis, q);
23
25
  return [degreesFromRadians(w), oaxis[0], oaxis[1], oaxis[2]];
24
26
  };
27
+ publicAPI.getOrientationQuaternion = function () {
28
+ let out = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
29
+ return mat4.getRotation(out, model.rotation);
30
+ };
25
31
  publicAPI.rotateX = val => {
26
32
  if (val === 0.0) {
27
33
  return;
@@ -57,6 +63,14 @@ function vtkProp3D(publicAPI, model) {
57
63
  mat4.multiply(model.rotation, model.rotation, quatMat);
58
64
  publicAPI.modified();
59
65
  };
66
+ publicAPI.rotateQuaternion = orientationQuaternion => {
67
+ if (Math.abs(orientationQuaternion[3]) >= 1 - VTK_EPSILON) {
68
+ return;
69
+ }
70
+ const oriQuatMat = mat4.fromQuat(new Float64Array(16), orientationQuaternion);
71
+ mat4.multiply(model.rotation, model.rotation, oriQuatMat);
72
+ publicAPI.modified();
73
+ };
60
74
  publicAPI.setOrientation = (x, y, z) => {
61
75
  if (x === model.orientation[0] && y === model.orientation[1] && z === model.orientation[2]) {
62
76
  return false;
@@ -81,6 +81,20 @@ export interface IRenderWindowInteractorEvent {
81
81
  type: InteractorEventType;
82
82
  }
83
83
 
84
+ export interface I3DEvent {
85
+ gamepad: Gamepad;
86
+ position: DOMPointReadOnly;
87
+ orientation: DOMPointReadOnly;
88
+ targetPosition: DOMPointReadOnly;
89
+ targetOrientation: DOMPointReadOnly;
90
+ device: Device;
91
+ }
92
+
93
+ export interface IButton3DEvent extends I3DEvent {
94
+ pressed: boolean;
95
+ input: Input;
96
+ }
97
+
84
98
  export interface vtkRenderWindowInteractor extends vtkObject {
85
99
 
86
100
  /**
@@ -678,10 +692,11 @@ export interface vtkRenderWindowInteractor extends vtkObject {
678
692
  animationEvent(args: any): any;
679
693
 
680
694
  /**
681
- *
695
+ * Triggers the 'Button3D' event.
696
+ *
682
697
  * @param args
683
698
  */
684
- button3DEvent(args: any): any;
699
+ button3DEvent(eventPayload: IButton3DEvent): void;
685
700
 
686
701
  /**
687
702
  *
@@ -804,10 +819,11 @@ export interface vtkRenderWindowInteractor extends vtkObject {
804
819
  mouseWheelEvent(args: any): any;
805
820
 
806
821
  /**
807
- *
808
- * @param args
822
+ * Triggers the 'Move3D' event.
823
+ *
824
+ * @param eventPayload
809
825
  */
810
- move3DEvent(args: any): any;
826
+ move3DEvent(eventPayload: I3DEvent): void;
811
827
 
812
828
  /**
813
829
  *
@@ -481,45 +481,51 @@ function vtkRenderWindowInteractor(publicAPI, model) {
481
481
  // watch for when buttons change state and fire events
482
482
  xrSession.inputSources.forEach(inputSource => {
483
483
  const gripPose = inputSource.gripSpace == null ? null : xrFrame.getPose(inputSource.gripSpace, xrRefSpace);
484
- const gp = inputSource.gamepad;
484
+ const targetRayPose = inputSource.gripSpace == null ? null : xrFrame.getPose(inputSource.targetRaySpace, xrRefSpace);
485
+ const gamepad = inputSource.gamepad;
485
486
  const hand = inputSource.handedness;
486
- if (gp) {
487
- if (!(gp.index in model.lastGamepadValues)) {
488
- model.lastGamepadValues[gp.index] = {
489
- left: {
490
- buttons: {}
491
- },
492
- right: {
493
- buttons: {}
494
- },
495
- none: {
496
- buttons: {}
497
- }
498
- };
499
- }
500
- for (let b = 0; b < gp.buttons.length; ++b) {
501
- if (!(b in model.lastGamepadValues[gp.index][hand].buttons)) {
502
- model.lastGamepadValues[gp.index][hand].buttons[b] = false;
503
- }
504
- if (model.lastGamepadValues[gp.index][hand].buttons[b] !== gp.buttons[b].pressed && gripPose != null) {
505
- publicAPI.button3DEvent({
506
- gamepad: gp,
507
- position: gripPose.transform.position,
508
- orientation: gripPose.transform.orientation,
509
- pressed: gp.buttons[b].pressed,
510
- device: inputSource.handedness === 'left' ? Device.LeftController : Device.RightController,
511
- input: deviceInputMap[gp.mapping] && deviceInputMap[gp.mapping][b] ? deviceInputMap[gp.mapping][b] : Input.Trigger
512
- });
513
- model.lastGamepadValues[gp.index][hand].buttons[b] = gp.buttons[b].pressed;
514
- }
515
- if (model.lastGamepadValues[gp.index][hand].buttons[b] && gripPose != null) {
516
- publicAPI.move3DEvent({
517
- gamepad: gp,
518
- position: gripPose.transform.position,
519
- orientation: gripPose.transform.orientation,
520
- device: inputSource.handedness === 'left' ? Device.LeftController : Device.RightController
521
- });
487
+ if (!gamepad) {
488
+ return;
489
+ }
490
+ if (!(gamepad.index in model.lastGamepadValues)) {
491
+ model.lastGamepadValues[gamepad.index] = {
492
+ left: {
493
+ buttons: {}
494
+ },
495
+ right: {
496
+ buttons: {}
497
+ },
498
+ none: {
499
+ buttons: {}
522
500
  }
501
+ };
502
+ }
503
+ for (let buttonIdx = 0; buttonIdx < gamepad.buttons.length; ++buttonIdx) {
504
+ if (!(buttonIdx in model.lastGamepadValues[gamepad.index][hand].buttons)) {
505
+ model.lastGamepadValues[gamepad.index][hand].buttons[buttonIdx] = false;
506
+ }
507
+ if (model.lastGamepadValues[gamepad.index][hand].buttons[buttonIdx] !== gamepad.buttons[buttonIdx].pressed && gripPose != null) {
508
+ publicAPI.button3DEvent({
509
+ gamepad,
510
+ position: gripPose.transform.position,
511
+ orientation: gripPose.transform.orientation,
512
+ targetPosition: targetRayPose.transform.position,
513
+ targetOrientation: targetRayPose.transform.orientation,
514
+ pressed: gamepad.buttons[buttonIdx].pressed,
515
+ device: inputSource.handedness === 'left' ? Device.LeftController : Device.RightController,
516
+ input: deviceInputMap[gamepad.mapping] && deviceInputMap[gamepad.mapping][buttonIdx] ? deviceInputMap[gamepad.mapping][buttonIdx] : Input.Trigger
517
+ });
518
+ model.lastGamepadValues[gamepad.index][hand].buttons[buttonIdx] = gamepad.buttons[buttonIdx].pressed;
519
+ }
520
+ if (model.lastGamepadValues[gamepad.index][hand].buttons[buttonIdx] && gripPose != null) {
521
+ publicAPI.move3DEvent({
522
+ gamepad,
523
+ position: gripPose.transform.position,
524
+ orientation: gripPose.transform.orientation,
525
+ targetPosition: targetRayPose.transform.position,
526
+ targetOrientation: targetRayPose.transform.orientation,
527
+ device: inputSource.handedness === 'left' ? Device.LeftController : Device.RightController
528
+ });
523
529
  }
524
530
  }
525
531
  });
@@ -1,6 +1,10 @@
1
1
  import { m as macro } from '../../macros2.js';
2
2
  import Constants from './RenderWindowHelper/Constants.js';
3
+ import vtkActor from '@kitware/vtk.js/Rendering/Core/Actor';
4
+ import vtkLineSource from '@kitware/vtk.js/Filters/Sources/LineSource';
5
+ import vtkMapper from '@kitware/vtk.js/Rendering/Core/Mapper';
3
6
  import { GET_UNDERLYING_CONTEXT } from '../OpenGL/RenderWindow/ContextProxy.js';
7
+ import { vec3 } from 'gl-matrix';
4
8
 
5
9
  const {
6
10
  XrSessionTypes
@@ -11,6 +15,23 @@ const DEFAULT_RESET_FACTORS = {
11
15
  translateZ: -1.5 // default translation initializes object in front of camera
12
16
  };
13
17
 
18
+ function createRay() {
19
+ const targetRayLineSource = vtkLineSource.newInstance();
20
+ const targetRayMapper = vtkMapper.newInstance();
21
+ targetRayMapper.setInputConnection(targetRayLineSource.getOutputPort());
22
+ const targetRayActor = vtkActor.newInstance();
23
+ targetRayActor.getProperty().setColor(1, 0, 0);
24
+ targetRayActor.getProperty().setLineWidth(5);
25
+ targetRayActor.setMapper(targetRayMapper);
26
+ targetRayActor.setPickable(false);
27
+ return {
28
+ lineSource: targetRayLineSource,
29
+ mapper: targetRayMapper,
30
+ actor: targetRayActor,
31
+ visible: false
32
+ };
33
+ }
34
+
14
35
  // ----------------------------------------------------------------------------
15
36
  // vtkWebXRRenderWindowHelper methods
16
37
  // ----------------------------------------------------------------------------
@@ -145,6 +166,36 @@ function vtkWebXRRenderWindowHelper(publicAPI, model) {
145
166
  model.xrRender = async (t, frame) => {
146
167
  const xrSession = frame.session;
147
168
  const isXrSessionHMD = [XrSessionTypes.HmdVR, XrSessionTypes.HmdAR].includes(model.xrSessionType);
169
+ if (isXrSessionHMD && model.drawControllersRay && model.xrReferenceSpace) {
170
+ const renderer = model.renderWindow.getRenderable().getRenderers()[0];
171
+ const camera = renderer.getActiveCamera();
172
+ const physicalToWorldMatrix = [];
173
+ camera.getPhysicalToWorldMatrix(physicalToWorldMatrix);
174
+ xrSession.inputSources.forEach(inputSource => {
175
+ if (inputSource.targetRaySpace == null || inputSource.gripSpace == null || inputSource.targetRayMode !== 'tracked-pointer') {
176
+ return;
177
+ }
178
+ if (model.inputSourceToRay[inputSource.handedness] == null) {
179
+ model.inputSourceToRay[inputSource.handedness] = createRay();
180
+ }
181
+ const ray = model.inputSourceToRay[inputSource.handedness];
182
+ const targetRayPose = frame.getPose(inputSource.targetRaySpace, model.xrReferenceSpace);
183
+ if (targetRayPose == null) {
184
+ return;
185
+ }
186
+ const targetRayPosition = vec3.fromValues(targetRayPose.transform.position.x, targetRayPose.transform.position.y, targetRayPose.transform.position.z);
187
+ const dir = camera.physicalOrientationToWorldDirection([targetRayPose.transform.orientation.x, targetRayPose.transform.orientation.y, targetRayPose.transform.orientation.z, targetRayPose.transform.orientation.w]);
188
+ const targetRayWorldPosition = vec3.transformMat4([], targetRayPosition, physicalToWorldMatrix);
189
+ const dist = renderer.getActiveCamera().getClippingRange()[1];
190
+ if (!ray.visible) {
191
+ renderer.addActor(ray.actor);
192
+ ray.visible = true;
193
+ }
194
+ ray.lineSource.setPoint1(targetRayWorldPosition[0] - dir[0] * dist, targetRayWorldPosition[1] - dir[1] * dist, targetRayWorldPosition[2] - dir[2] * dist);
195
+ ray.lineSource.setPoint2(...targetRayWorldPosition);
196
+ });
197
+ model.renderWindow.render();
198
+ }
148
199
  model.renderWindow.getRenderable().getInteractor().updateXRGamepads(xrSession, frame, model.xrReferenceSpace);
149
200
  model.xrSceneFrame = model.xrSession.requestAnimationFrame(model.xrRender);
150
201
  const xrPose = frame.getViewerPose(model.xrReferenceSpace);
@@ -202,27 +253,31 @@ function vtkWebXRRenderWindowHelper(publicAPI, model) {
202
253
  // Object factory
203
254
  // ----------------------------------------------------------------------------
204
255
 
205
- const DEFAULT_VALUES = {
206
- initialized: false,
207
- initCanvasSize: null,
208
- initBackground: null,
209
- renderWindow: null,
210
- xrSession: null,
211
- xrSessionType: 0,
212
- xrReferenceSpace: null
213
- };
256
+ function defaultValues() {
257
+ return {
258
+ initialized: false,
259
+ drawControllersRay: false,
260
+ inputSourceToRay: {},
261
+ initCanvasSize: null,
262
+ initBackground: null,
263
+ renderWindow: null,
264
+ xrSession: null,
265
+ xrSessionType: 0,
266
+ xrReferenceSpace: null
267
+ };
268
+ }
214
269
 
215
270
  // ----------------------------------------------------------------------------
216
271
 
217
272
  function extend(publicAPI, model) {
218
273
  let initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
219
- Object.assign(model, DEFAULT_VALUES, initialValues);
274
+ Object.assign(model, defaultValues(), initialValues);
220
275
 
221
276
  // Build VTK API
222
277
  macro.obj(publicAPI, model);
223
278
  macro.event(publicAPI, model, 'event');
224
279
  macro.get(publicAPI, model, ['xrSession']);
225
- macro.setGet(publicAPI, model, ['renderWindow']);
280
+ macro.setGet(publicAPI, model, ['renderWindow', 'drawControllersRay']);
226
281
 
227
282
  // Object methods
228
283
  vtkWebXRRenderWindowHelper(publicAPI, model);
package/index.d.ts CHANGED
@@ -123,6 +123,7 @@
123
123
  /// <reference path="./Interaction/Manipulators/MouseCameraTrackballZoomManipulator.d.ts" />
124
124
  /// <reference path="./Interaction/Manipulators/MouseCameraTrackballZoomToMouseManipulator.d.ts" />
125
125
  /// <reference path="./Interaction/Manipulators/MouseRangeManipulator.d.ts" />
126
+ /// <reference path="./Interaction/Style/InteractorStyleHMDXR.d.ts" />
126
127
  /// <reference path="./Interaction/Style/InteractorStyleImage.d.ts" />
127
128
  /// <reference path="./Interaction/Style/InteractorStyleManipulator.d.ts" />
128
129
  /// <reference path="./Interaction/Style/InteractorStyleTrackballCamera.d.ts" />
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kitware/vtk.js",
3
- "version": "30.0.0",
3
+ "version": "30.1.0",
4
4
  "description": "Visualization Toolkit for the Web",
5
5
  "keywords": [
6
6
  "3d",