iMSTK
Interactive Medical Simulation Toolkit
imstkVtkOpenVRRenderWindowInteractorImstk.cpp
1 /*
2 ** This file is part of the Interactive Medical Simulation Toolkit (iMSTK)
3 ** iMSTK is distributed under the Apache License, Version 2.0.
4 ** See accompanying NOTICE for details.
5 */
6 
7 #include <cassert>
8 #include <cmath>
9 #include <cstdio>
10 #include <cstdlib>
11 #include <cstring>
12 
13 #include "vtkOpenGLState.h"
14 #include "vtkOpenVROverlay.h"
15 #include "vtkOpenVRRenderWindow.h"
16 #include "imstkVtkOpenVRRenderWindowInteractorImstk.h"
17 #include "vtkRendererCollection.h"
18 #include "vtkVRRenderWindow.h"
19 
20 #include "vtkEventData.h"
21 
22 #include "vtkActor.h"
23 #include "vtkCommand.h"
24 #include "vtkMatrix4x4.h"
25 #include "vtkNew.h"
26 #include "vtkObjectFactory.h"
27 #include "vtkOpenVRCamera.h"
28 #include "vtkOpenVRInteractorStyle.h"
29 #include "vtkTextureObject.h"
30 #include "vtkTransform.h"
31 #include <vtksys/SystemTools.hxx>
32 
33 vtkStandardNewMacro(vtkOpenVRRenderWindowInteractorImstk);
34 
35 void(*vtkOpenVRRenderWindowInteractorImstk::ClassExitMethod)(void*) = (void (*)(void*))nullptr;
36 void* vtkOpenVRRenderWindowInteractorImstk::ClassExitMethodArg = (void*)nullptr;
37 void(*vtkOpenVRRenderWindowInteractorImstk::ClassExitMethodArgDelete)(
38  void*) = (void (*)(void*))nullptr;
39 
40 //------------------------------------------------------------------------------
41 // Construct object so that light follows camera motion.
42 vtkOpenVRRenderWindowInteractorImstk::vtkOpenVRRenderWindowInteractorImstk()
43 {
44  vtkNew<vtkOpenVRInteractorStyle> style;
45  this->SetInteractorStyle(style);
46 
47  for (int i = 0; i < vtkEventDataNumberOfDevices; i++)
48  {
49  this->DeviceInputDownCount[i] = 0;
50  }
51  this->ActionManifestFileName = "./vtk_openvr_actions.json";
52  this->ActionSetName = "/actions/vtk";
53 }
54 
55 //------------------------------------------------------------------------------
56 vtkOpenVRRenderWindowInteractorImstk::~vtkOpenVRRenderWindowInteractorImstk() = default;
57 
58 void
59 vtkOpenVRRenderWindowInteractorImstk::SetPhysicalScale(double scale)
60 {
61  vtkVRRenderWindow* win = vtkVRRenderWindow::SafeDownCast(this->RenderWindow);
62  win->SetPhysicalScale(scale);
63 }
64 
65 double
66 vtkOpenVRRenderWindowInteractorImstk::GetPhysicalScale()
67 {
68  vtkVRRenderWindow* win = vtkVRRenderWindow::SafeDownCast(this->RenderWindow);
69  return win->GetPhysicalScale();
70 }
71 
72 void
74  vtkCamera*, double t1, double t2, double t3)
75 {
76  vtkVRRenderWindow* win = vtkVRRenderWindow::SafeDownCast(this->RenderWindow);
77  win->SetPhysicalTranslation(t1, t2, t3);
78 }
79 
80 double*
81 vtkOpenVRRenderWindowInteractorImstk::GetPhysicalTranslation(vtkCamera*)
82 {
83  vtkVRRenderWindow* win = vtkVRRenderWindow::SafeDownCast(this->RenderWindow);
84  return win->GetPhysicalTranslation();
85 }
86 
87 void
88 vtkOpenVRRenderWindowInteractorImstk::ConvertOpenVRPoseToMatrices(
89  const vr::TrackedDevicePose_t& tdPose, vtkMatrix4x4* poseMatrixWorld,
90  vtkMatrix4x4* poseMatrixPhysical /*=nullptr*/)
91 {
92  if (!poseMatrixWorld && !poseMatrixPhysical)
93  {
94  return;
95  }
96 
97  vtkNew<vtkMatrix4x4> poseMatrixPhysicalTemp;
98  for (int row = 0; row < 3; ++row)
99  {
100  for (int col = 0; col < 4; ++col)
101  {
102  poseMatrixPhysicalTemp->SetElement(row, col, tdPose.mDeviceToAbsoluteTracking.m[row][col]);
103  }
104  }
105  if (poseMatrixPhysical)
106  {
107  poseMatrixPhysical->DeepCopy(poseMatrixPhysicalTemp);
108  }
109 
110  if (poseMatrixWorld)
111  {
112  vtkVRRenderWindow* win = vtkVRRenderWindow::SafeDownCast(this->RenderWindow);
113  vtkNew<vtkMatrix4x4> physicalToWorldMatrix;
114  win->GetPhysicalToWorldMatrix(physicalToWorldMatrix);
115  vtkMatrix4x4::Multiply4x4(physicalToWorldMatrix, poseMatrixPhysicalTemp, poseMatrixWorld);
116  }
117 }
118 
119 void
120 vtkOpenVRRenderWindowInteractorImstk::ConvertPoseToWorldCoordinates(
121  const vr::TrackedDevicePose_t& tdPose,
122  double pos[3], // Output world position
123  double wxyz[4], // Output world orientation quaternion
124  double ppos[3], // Output physical position
125  double wdir[3]) // Output world view direction (-Z)
126 {
127  // Convenience function to use the openvr-independant function
128  // TODO: remove it
129  this->ConvertPoseMatrixToWorldCoordinates(
130  tdPose.mDeviceToAbsoluteTracking.m, pos, wxyz, ppos, wdir);
131 }
132 
133 void
134 vtkOpenVRRenderWindowInteractorImstk::ConvertPoseMatrixToWorldCoordinates(
135  const float poseMatrix[3][4],
136  double pos[3], // Output world position
137  double wxyz[4], // Output world orientation quaternion
138  double ppos[3], // Output physical position
139  double wdir[3]) // Output world view direction (-Z)
140 {
141  // TODO: define it in generic superclass VRRenderWindowInteractor
142  vtkVRRenderWindow* win = vtkVRRenderWindow::SafeDownCast(this->RenderWindow);
143  double physicalScale = win->GetPhysicalScale();
144  double* trans = win->GetPhysicalTranslation();
145 
146  // Vive to world axes
147  double* vup = win->GetPhysicalViewUp();
148  double* dop = win->GetPhysicalViewDirection();
149  double vright[3];
150  vtkMath::Cross(dop, vup, vright);
151 
152  // extract HMD axes
153  double hvright[3];
154  hvright[0] = poseMatrix[0][0];
155  hvright[1] = poseMatrix[1][0];
156  hvright[2] = poseMatrix[2][0];
157  double hvup[3];
158  hvup[0] = poseMatrix[0][1];
159  hvup[1] = poseMatrix[1][1];
160  hvup[2] = poseMatrix[2][1];
161 
162  // convert position to world coordinates
163  // get the position and orientation of the button press
164  for (int i = 0; i < 3; i++)
165  {
166  ppos[i] = poseMatrix[i][3];
167  }
168 
169  pos[0] = ppos[0] * vright[0] + ppos[1] * vup[0] - ppos[2] * dop[0];
170  pos[1] = ppos[0] * vright[1] + ppos[1] * vup[1] - ppos[2] * dop[1];
171  pos[2] = ppos[0] * vright[2] + ppos[1] * vup[2] - ppos[2] * dop[2];
172  // now adjust for scale and translation
173  for (int i = 0; i < 3; i++)
174  {
175  pos[i] = pos[i] * physicalScale - trans[i];
176  }
177 
178  // convert axes to world coordinates
179  double fvright[3]; // final vright
180  fvright[0] = hvright[0] * vright[0] + hvright[1] * vup[0] - hvright[2] * dop[0];
181  fvright[1] = hvright[0] * vright[1] + hvright[1] * vup[1] - hvright[2] * dop[1];
182  fvright[2] = hvright[0] * vright[2] + hvright[1] * vup[2] - hvright[2] * dop[2];
183  double fvup[3]; // final vup
184  fvup[0] = hvup[0] * vright[0] + hvup[1] * vup[0] - hvup[2] * dop[0];
185  fvup[1] = hvup[0] * vright[1] + hvup[1] * vup[1] - hvup[2] * dop[1];
186  fvup[2] = hvup[0] * vright[2] + hvup[1] * vup[2] - hvup[2] * dop[2];
187  vtkMath::Cross(fvup, fvright, wdir);
188 
189  double ortho[3][3];
190  for (int i = 0; i < 3; i++)
191  {
192  ortho[i][0] = fvright[i];
193  ortho[i][1] = fvup[i];
194  ortho[i][2] = -wdir[i];
195  }
196 
197  vtkMath::Matrix3x3ToQuaternion(ortho, wxyz);
198 
199  // calc the return value wxyz
200  double mag = sqrt(wxyz[1] * wxyz[1] + wxyz[2] * wxyz[2] + wxyz[3] * wxyz[3]);
201 
202  if (mag != 0.0)
203  {
204  wxyz[0] = 2.0 * vtkMath::DegreesFromRadians(atan2(mag, wxyz[0]));
205  wxyz[1] /= mag;
206  wxyz[2] /= mag;
207  wxyz[3] /= mag;
208  }
209  else
210  {
211  wxyz[0] = 0.0;
212  wxyz[1] = 0.0;
213  wxyz[2] = 0.0;
214  wxyz[3] = 1.0;
215  }
216 }
217 
218 //---------------------------------------------------------------------------------------------------------------------
219 // Purpose: Returns true if the action is active and its state is true
220 //---------------------------------------------------------------------------------------------------------------------
221 static bool
222 GetDigitalActionState(
223  vr::VRActionHandle_t action, vr::VRInputValueHandle_t* pDevicePath = nullptr)
224 {
225  vr::InputDigitalActionData_t actionData;
226  vr::VRInput()->GetDigitalActionData(
227  action, &actionData, sizeof(actionData), vr::k_ulInvalidInputValueHandle);
228  if (pDevicePath)
229  {
230  *pDevicePath = vr::k_ulInvalidInputValueHandle;
231  if (actionData.bActive)
232  {
233  vr::InputOriginInfo_t originInfo;
234  if (vr::VRInputError_None ==
235  vr::VRInput()->GetOriginTrackedDeviceInfo(
236  actionData.activeOrigin, &originInfo, sizeof(originInfo)))
237  {
238  *pDevicePath = originInfo.devicePath;
239  }
240  }
241  }
242  return actionData.bActive && actionData.bState;
243 }
244 
245 //------------------------------------------------------------------------------
246 void
248 {
249  this->StartedMessageLoop = 1;
250  this->Done = false;
251 
252  vtkOpenVRRenderWindow* renWin = vtkOpenVRRenderWindow::SafeDownCast(this->RenderWindow);
253 
254  static_cast<vtkRenderer*>(renWin->GetRenderers()->GetItemAsObject(0));
255 
256  while (!this->Done)
257  {
258  ProcessEvents();
259  }
260 }
261 
262 void
264 {
265  vtkOpenVRRenderWindow* renWin = vtkOpenVRRenderWindow::SafeDownCast(this->RenderWindow);
266 
267  vtkRenderer* ren = static_cast<vtkRenderer*>(renWin->GetRenderers()->GetItemAsObject(0));
268  this->DoOneEvent(renWin, ren, true);
269 }
270 
271 void
272 vtkOpenVRRenderWindowInteractorImstk::DoOneEvent(vtkOpenVRRenderWindow* renWin, vtkRenderer* ren, bool doRender)
273 {
274  if (!renWin || !ren)
275  {
276  return;
277  }
278  vr::IVRSystem* pHMD = renWin->GetHMD();
279 
280  if (!pHMD)
281  {
282  // try rendering to create the HMD connection
283  renWin->Render();
284  return;
285  }
286 
287  vr::VREvent_t event;
288  vtkOpenVROverlay* ovl = renWin->GetDashboardOverlay();
289  bool result = false;
290 
291  if (vr::VROverlay() && vr::VROverlay()->IsOverlayVisible(ovl->GetOverlayHandle()))
292  {
293  result =
294  vr::VROverlay()->PollNextOverlayEvent(ovl->GetOverlayHandle(), &event, sizeof(vr::VREvent_t));
295 
296  if (result)
297  {
298  int height = ovl->GetOverlayTexture()->GetHeight();
299  switch (event.eventType)
300  {
301  case vr::VREvent_MouseButtonDown:
302  {
303  if (event.data.mouse.button == vr::VRMouseButton_Left)
304  {
305  ovl->MouseButtonPress(event.data.mouse.x, height - event.data.mouse.y - 1);
306  }
307  }
308  break;
309 
310  case vr::VREvent_MouseButtonUp:
311  {
312  if (event.data.mouse.button == vr::VRMouseButton_Left)
313  {
314  ovl->MouseButtonRelease(event.data.mouse.x, height - event.data.mouse.y - 1);
315  }
316  }
317  break;
318 
319  case vr::VREvent_MouseMove:
320  {
321  ovl->MouseMoved(event.data.mouse.x, height - event.data.mouse.y - 1);
322  }
323  break;
324 
325  case vr::VREvent_OverlayShown:
326  {
327  renWin->RenderOverlay();
328  }
329  break;
330 
331  case vr::VREvent_Quit:
332  this->Done = true;
333  break;
334  }
335  }
336 
337  // eat up any pending events
338  while (pHMD->PollNextEvent(&event, sizeof(vr::VREvent_t)))
339  {
340  }
341  }
342  else
343  {
344  result = pHMD->PollNextEvent(&event, sizeof(vr::VREvent_t));
345 
346  // process all pending events
347  while (result)
348  {
349  result = pHMD->PollNextEvent(&event, sizeof(vr::VREvent_t));
350  }
351 
352  // Process SteamVR action state
353  // UpdateActionState is called each frame to update the state of the actions themselves. The
354  // application controls which action sets are active with the provided array of
355  // VRActiveActionSet_t structs.
356  vr::VRActiveActionSet_t actionSet = { 0 };
357  actionSet.ulActionSet = this->ActionsetVTK;
358  vr::VRInput()->UpdateActionState(&actionSet, sizeof(actionSet), 1);
359 
360  for (int tracker = 0; tracker < vtkOpenVRRenderWindowInteractorImstk::NumberOfTrackers; tracker++)
361  {
362  vr::InputOriginInfo_t originInfo;
363  vr::EVRInputError evriError = vr::VRInput()->GetOriginTrackedDeviceInfo(
364  this->Trackers[tracker].Source, &originInfo, sizeof(vr::InputOriginInfo_t));
365  if (evriError != vr::VRInputError_None)
366  {
367  // this can happen when a tracker isn't online
368  continue;
369  }
370 
371  vr::EVRCompositorError evrcError = vr::VRCompositor()->GetLastPoseForTrackedDeviceIndex(
372  originInfo.trackedDeviceIndex, &this->Trackers[tracker].LastPose, nullptr);
373  if (evrcError != vr::VRCompositorError_None)
374  {
375  vtkErrorMacro("Error in GetLastPoseForTrackedDeviceIndex: " << evrcError);
376  continue;
377  }
378 
379  if (this->Trackers[tracker].LastPose.bPoseIsValid)
380  {
381  double pos[3] = { 0.0 };
382  double ppos[3] = { 0.0 };
383  double wxyz[4] = { 0.0 };
384  double wdir[3] = { 0.0 };
385  this->ConvertPoseToWorldCoordinates(
386  this->Trackers[tracker].LastPose, pos, wxyz, ppos, wdir);
387  vtkNew<vtkEventDataDevice3D> ed;
388  ed->SetWorldPosition(pos);
389  ed->SetWorldOrientation(wxyz);
390  ed->SetWorldDirection(wdir);
391 
392  switch (tracker)
393  {
394  case LeftHand:
395  ed->SetDevice(vtkEventDataDevice::LeftController);
396  break;
397  case RightHand:
398  ed->SetDevice(vtkEventDataDevice::RightController);
399  break;
400  case Head:
401  ed->SetDevice(vtkEventDataDevice::HeadMountedDisplay);
402  break;
403  }
404  ed->SetType(vtkCommand::Move3DEvent);
405 
406  // would like to remove the three following lines, they are there mostly to support
407  // multitouch and event handling where the handler doesn;t use the data in the event
408  // the first doesn't seem to be a common use pattern for VR, the second isn't used
409  // to my knowledge
410  this->SetPointerIndex(static_cast<int>(ed->GetDevice()));
411  this->SetPhysicalEventPosition(ppos[0], ppos[1], ppos[2], this->PointerIndex);
412  this->SetWorldEventPosition(pos[0], pos[1], pos[2], this->PointerIndex);
413  this->SetWorldEventOrientation(wxyz[0], wxyz[1], wxyz[2], wxyz[3], this->PointerIndex);
414 
415  if (this->Enabled)
416  {
417  this->InvokeEvent(vtkCommand::Move3DEvent, ed);
418  }
419  }
420  }
421 
422  // handle other actions
423  for (auto& it : this->ActionMap)
424  {
425  vr::VRActionHandle_t& actionHandle = it.second.ActionHandle;
426  vtkEventDataDevice3D* edp = nullptr;
427  vr::VRInputValueHandle_t activeOrigin = 0;
428 
429  if (it.second.IsAnalog)
430  {
431  vr::InputAnalogActionData_t analogData;
432  if (vr::VRInput()->GetAnalogActionData(actionHandle, &analogData, sizeof(analogData),
433  vr::k_ulInvalidInputValueHandle) == vr::VRInputError_None
434  && analogData.bActive)
435  {
436  // send a movement event
437  edp = vtkEventDataDevice3D::New();
438  edp->SetTrackPadPosition(analogData.x, analogData.y);
439  activeOrigin = analogData.activeOrigin;
440  }
441  }
442  else
443  {
444  vr::InputDigitalActionData_t actionData;
445  auto vrresult = vr::VRInput()->GetDigitalActionData(
446  actionHandle, &actionData, sizeof(actionData), vr::k_ulInvalidInputValueHandle);
447  if (vrresult == vr::VRInputError_None && actionData.bActive && actionData.bChanged)
448  {
449  edp = vtkEventDataDevice3D::New();
450  edp->SetAction(
451  actionData.bState ? vtkEventDataAction::Press : vtkEventDataAction::Release);
452  activeOrigin = actionData.activeOrigin;
453  }
454  }
455 
456  if (edp)
457  {
458  double pos[3] = { 0.0 };
459  double ppos[3] = { 0.0 };
460  double wxyz[4] = { 0.0 };
461  double wdir[3] = { 0.0 };
462 
463  vr::InputBindingInfo_t inBindingInfo;
464  uint32_t returnedBindingInfoCount = 0;
465  /*vr::EVRInputError abinfo = */ vr::VRInput()->GetActionBindingInfo(
466  actionHandle, &inBindingInfo, sizeof(inBindingInfo), 1, &returnedBindingInfoCount);
467 
468  std::string inputSource = inBindingInfo.rchInputSourceType;
469  if (inputSource == "trackpad")
470  {
471  edp->SetInput(vtkEventDataDeviceInput::TrackPad);
472  }
473  else if (inputSource == "joystick")
474  {
475  edp->SetInput(vtkEventDataDeviceInput::Joystick);
476  }
477  else if (inputSource == "trigger")
478  {
479  edp->SetInput(vtkEventDataDeviceInput::Trigger);
480  }
481  else if (inputSource == "grip")
482  {
483  edp->SetInput(vtkEventDataDeviceInput::Grip);
484  }
485  else
486  {
487  edp->SetInput(vtkEventDataDeviceInput::Unknown);
488  }
489 
490  vr::InputOriginInfo_t originInfo;
491  if (vr::VRInputError_None ==
492  vr::VRInput()->GetOriginTrackedDeviceInfo(activeOrigin, &originInfo, sizeof(originInfo)))
493  {
494  if (originInfo.devicePath == this->Trackers[LeftHand].Source)
495  {
496  edp->SetDevice(vtkEventDataDevice::LeftController);
497  this->ConvertPoseToWorldCoordinates(this->Trackers[0].LastPose, pos, wxyz, ppos, wdir);
498  }
499  if (originInfo.devicePath == this->Trackers[RightHand].Source)
500  {
501  edp->SetDevice(vtkEventDataDevice::RightController);
502  this->ConvertPoseToWorldCoordinates(this->Trackers[1].LastPose, pos, wxyz, ppos, wdir);
503  }
504  // edp->SetInput(originInfo.);
505  }
506  edp->SetWorldPosition(pos);
507  edp->SetWorldOrientation(wxyz);
508  edp->SetWorldDirection(wdir);
509  edp->SetType(it.second.EventId);
510 
511  if (it.second.UseFunction)
512  {
513  it.second.Function(edp);
514  }
515  else
516  {
517  this->InvokeEvent(it.second.EventId, edp);
518  }
519  edp->Delete();
520  }
521  }
522 
523  if (this->RecognizeGestures)
524  {
525  this->RecognizeComplexGesture(nullptr);
526  }
527  if (doRender)
528  {
529  this->InvokeEvent(vtkCommand::RenderEvent);
530  auto ostate = renWin->GetState();
531  renWin->MakeCurrent();
532  ostate->Reset();
533  ostate->Push();
534  renWin->Render();
535  ostate->Pop();
536  }
537  }
538 }
539 
540 void
541 vtkOpenVRRenderWindowInteractorImstk::HandleGripEvents(vtkEventData* ed)
542 {
543  vtkEventDataDevice3D* edata = ed->GetAsEventDataDevice3D();
544  if (!edata)
545  {
546  return;
547  }
548 
549  this->PointerIndex = static_cast<int>(edata->GetDevice());
550  if (edata->GetAction() == vtkEventDataAction::Press)
551  {
552  this->DeviceInputDownCount[this->PointerIndex] = 1;
553 
554  this->StartingPhysicalEventPositions[this->PointerIndex][0] =
555  this->PhysicalEventPositions[this->PointerIndex][0];
556  this->StartingPhysicalEventPositions[this->PointerIndex][1] =
557  this->PhysicalEventPositions[this->PointerIndex][1];
558  this->StartingPhysicalEventPositions[this->PointerIndex][2] =
559  this->PhysicalEventPositions[this->PointerIndex][2];
560 
561  vtkVRRenderWindow* renWin = vtkVRRenderWindow::SafeDownCast(this->RenderWindow);
562  renWin->GetPhysicalToWorldMatrix(this->StartingPhysicalToWorldMatrix);
563 
564  // Both controllers have the grip down, start multitouch
565  if (this->DeviceInputDownCount[static_cast<int>(vtkEventDataDevice::LeftController)]
566  && this->DeviceInputDownCount[static_cast<int>(vtkEventDataDevice::RightController)])
567  {
568  // we do not know what the gesture is yet
569  this->CurrentGesture = vtkCommand::StartEvent;
570  }
571  return;
572  }
573  // end the gesture if needed
574  if (edata->GetAction() == vtkEventDataAction::Release)
575  {
576  this->DeviceInputDownCount[this->PointerIndex] = 0;
577 
578  if (edata->GetInput() == vtkEventDataDeviceInput::Grip)
579  {
580  if (this->CurrentGesture == vtkCommand::PinchEvent)
581  {
582  this->EndPinchEvent();
583  }
584  if (this->CurrentGesture == vtkCommand::PanEvent)
585  {
586  this->EndPanEvent();
587  }
588  if (this->CurrentGesture == vtkCommand::RotateEvent)
589  {
590  this->EndRotateEvent();
591  }
592  this->CurrentGesture = vtkCommand::NoEvent;
593  return;
594  }
595  }
596 }
597 
598 //------------------------------------------------------------------------------
599 void
600 vtkOpenVRRenderWindowInteractorImstk::RecognizeComplexGesture(vtkEventDataDevice3D*)
601 {
602  // Recognize gesture only if one button is pressed per controller
603  int lhand = static_cast<int>(vtkEventDataDevice::LeftController);
604  int rhand = static_cast<int>(vtkEventDataDevice::RightController);
605 
606  if (this->DeviceInputDownCount[lhand] > 1 || this->DeviceInputDownCount[lhand] == 0
607  || this->DeviceInputDownCount[rhand] > 1 || this->DeviceInputDownCount[rhand] == 0)
608  {
609  this->CurrentGesture = vtkCommand::NoEvent;
610  return;
611  }
612 
613  double* posVals[2];
614  double* startVals[2];
615  posVals[0] = this->PhysicalEventPositions[lhand];
616  posVals[1] = this->PhysicalEventPositions[rhand];
617 
618  startVals[0] = this->StartingPhysicalEventPositions[lhand];
619  startVals[1] = this->StartingPhysicalEventPositions[rhand];
620 
621  // The meat of the algorithm
622  // on move events we analyze them to determine what type
623  // of movement it is and then deal with it.
624  if (this->CurrentGesture != vtkCommand::NoEvent)
625  {
626  // calculate the distances
627  double originalDistance = sqrt(vtkMath::Distance2BetweenPoints(startVals[0], startVals[1]));
628  double newDistance = sqrt(vtkMath::Distance2BetweenPoints(posVals[0], posVals[1]));
629 
630  // calculate the translations
631  double t0[3];
632  t0[0] = posVals[0][0] - startVals[0][0];
633  t0[1] = posVals[0][1] - startVals[0][1];
634  t0[2] = posVals[0][2] - startVals[0][2];
635 
636  double t1[3];
637  t1[0] = posVals[1][0] - startVals[1][0];
638  t1[1] = posVals[1][1] - startVals[1][1];
639  t1[2] = posVals[1][2] - startVals[1][2];
640 
641  double trans[3];
642  trans[0] = (t0[0] + t1[0]) / 2.0;
643  trans[1] = (t0[1] + t1[1]) / 2.0;
644  trans[2] = (t0[2] + t1[2]) / 2.0;
645 
646  // calculate rotations
647  double originalAngle = vtkMath::DegreesFromRadians(
648  atan2((double)startVals[1][2] - startVals[0][2], (double)startVals[1][0] - startVals[0][0]));
649  double newAngle = vtkMath::DegreesFromRadians(
650  atan2((double)posVals[1][2] - posVals[0][2], (double)posVals[1][0] - posVals[0][0]));
651 
652  // angles are cyclic so watch for that, -179 and 179 are only 2 apart :)
653  if (newAngle - originalAngle > 180.0)
654  {
655  newAngle -= 360;
656  }
657  if (newAngle - originalAngle < -180.0)
658  {
659  newAngle += 360;
660  }
661  double angleDeviation = newAngle - originalAngle;
662 
663  // do we know what gesture we are doing yet? If not
664  // see if we can figure it out
665  if (this->CurrentGesture == vtkCommand::StartEvent)
666  {
667  // pinch is a move to/from the center point
668  // rotate is a move along the circumference
669  // pan is a move of the center point
670  // compute the distance along each of these axes in meters
671  // the first to break thresh wins
672  double thresh = 0.05; // in meters
673 
674  double pinchDistance = fabs(newDistance - originalDistance);
675  double panDistance = sqrt(trans[0] * trans[0] + trans[1] * trans[1] + trans[2] * trans[2]);
676  double rotateDistance = originalDistance * 3.1415926 * fabs(angleDeviation) / 180.0;
677 
678  if (pinchDistance > thresh && pinchDistance > panDistance && pinchDistance > rotateDistance)
679  {
680  this->CurrentGesture = vtkCommand::PinchEvent;
681  this->Scale = 1.0;
682  this->StartPinchEvent();
683  }
684  else if (rotateDistance > thresh && rotateDistance > panDistance)
685  {
686  this->CurrentGesture = vtkCommand::RotateEvent;
687  this->Rotation = 0.0;
688  this->StartRotateEvent();
689  }
690  else if (panDistance > thresh)
691  {
692  this->CurrentGesture = vtkCommand::PanEvent;
693  this->Translation3D[0] = 0.0;
694  this->Translation3D[1] = 0.0;
695  this->Translation3D[2] = 0.0;
696  this->StartPanEvent();
697  }
698  }
699  // if we have found a specific type of movement then
700  // handle it
701  if (this->CurrentGesture == vtkCommand::RotateEvent)
702  {
703  this->SetRotation(angleDeviation);
704  this->RotateEvent();
705  }
706  if (this->CurrentGesture == vtkCommand::PinchEvent)
707  {
708  this->SetScale(newDistance / originalDistance);
709  this->PinchEvent();
710  }
711  if (this->CurrentGesture == vtkCommand::PanEvent)
712  {
713  // Vive to world axes
714  vtkVRRenderWindow* win = vtkVRRenderWindow::SafeDownCast(this->RenderWindow);
715  double* vup = win->GetPhysicalViewUp();
716  double* dop = win->GetPhysicalViewDirection();
717  double physicalScale = win->GetPhysicalScale();
718  double vright[3];
719  vtkMath::Cross(dop, vup, vright);
720  double wtrans[3];
721 
722  // convert translation to world coordinates
723  // now adjust for scale
724  for (int i = 0; i < 3; i++)
725  {
726  wtrans[i] = trans[0] * vright[i] + trans[1] * vup[i] - trans[2] * dop[i];
727  wtrans[i] = wtrans[i] * physicalScale;
728  }
729 
730  this->SetTranslation3D(wtrans);
731  this->PanEvent();
732  }
733  }
734 }
735 
736 //------------------------------------------------------------------------------
737 void
739  std::string path, vtkCommand::EventIds eid, bool isAnalog)
740 {
741  auto& am = this->ActionMap[path];
742  am.EventId = eid;
743  am.UseFunction = false;
744  am.IsAnalog = isAnalog;
745  if (this->Initialized)
746  {
747  vr::VRInput()->GetActionHandle(path.c_str(), &am.ActionHandle);
748 
749  // "path" : "/user/hand/right/input/trackpad"
750  }
751 }
752 
753 void
755  std::string path, bool isAnalog, std::function<void(vtkEventData*)> func)
756 {
757  auto& am = this->ActionMap[path];
758  am.UseFunction = true;
759  am.Function = func;
760  am.IsAnalog = isAnalog;
761  if (this->Initialized)
762  {
763  vr::VRInput()->GetActionHandle(path.c_str(), &am.ActionHandle);
764  }
765 }
766 
767 //------------------------------------------------------------------------------
768 void
770 {
771  // make sure we have a RenderWindow and camera
772  if (!this->RenderWindow)
773  {
774  vtkErrorMacro(<< "No renderer defined!");
775  return;
776  }
777  if (this->Initialized)
778  {
779  return;
780  }
781 
782  vtkVRRenderWindow* ren = vtkVRRenderWindow::SafeDownCast(this->RenderWindow);
783  int* size;
784 
785  this->Initialized = 1;
786  // get the info we need from the RenderingWindow
787 
788  size = ren->GetSize();
789  ren->GetPosition();
790  this->Enable();
791  this->Size[0] = size[0];
792  this->Size[1] = size[1];
793 
794  std::string fullpath = vtksys::SystemTools::CollapseFullPath(this->ActionManifestFileName);
795  vr::VRInput()->SetActionManifestPath(fullpath.c_str());
796  vr::VRInput()->GetActionSetHandle(this->ActionSetName.c_str(), &this->ActionsetVTK);
797 
798  // add in pose events
799  vr::VRInput()->GetInputSourceHandle("/user/hand/left", &this->Trackers[LeftHand].Source);
800  // vr::VRInput()->GetActionHandle("/actions/vtk/in/HandLeft",
801  // &this->Trackers[LeftHand].ActionPose);
802  vr::VRInput()->GetInputSourceHandle("/user/hand/right", &this->Trackers[RightHand].Source);
803  // vr::VRInput()->GetActionHandle(
804  // "/actions/vtk/in/HandRight", &this->Trackers[RightHand].ActionPose);
805  vr::VRInput()->GetInputSourceHandle("/user/head", &this->Trackers[Head].Source);
806  // vr::VRInput()->GetActionHandle("/actions/vtk/in/head", &this->Trackers[Head].ActionPose);
807 
808  this->AddAction("/actions/vtk/in/LeftGripAction", false,
809  [this](vtkEventData* ed) { this->HandleGripEvents(ed); });
810  this->AddAction("/actions/vtk/in/RightGripAction", false,
811  [this](vtkEventData* ed) { this->HandleGripEvents(ed); });
812 
813  // add extra event actions
814  for (auto& it : this->ActionMap)
815  {
816  vr::VRInput()->GetActionHandle(it.first.c_str(), &it.second.ActionHandle);
817  }
818 }
819 
820 //------------------------------------------------------------------------------
821 int
823  int vtkNotUsed(timerId), int vtkNotUsed(timerType), unsigned long vtkNotUsed(duration))
824 {
825  // todo
826  return 0;
827 }
828 
829 //------------------------------------------------------------------------------
830 int
831 vtkOpenVRRenderWindowInteractorImstk::InternalDestroyTimer(int vtkNotUsed(platformTimerId))
832 {
833  // todo
834  return 0;
835 }
836 
837 //------------------------------------------------------------------------------
838 // Specify the default function to be called when an interactor needs to exit.
839 // This callback is overridden by an instance ExitMethod that is defined.
840 void
842 {
844  || arg != vtkOpenVRRenderWindowInteractorImstk::ClassExitMethodArg)
845  {
846  // delete the current arg if there is a delete method
847  if ((vtkOpenVRRenderWindowInteractorImstk::ClassExitMethodArg)
848  && (vtkOpenVRRenderWindowInteractorImstk::ClassExitMethodArgDelete))
849  {
850  (*vtkOpenVRRenderWindowInteractorImstk::ClassExitMethodArgDelete)(
851  vtkOpenVRRenderWindowInteractorImstk::ClassExitMethodArg);
852  }
854  vtkOpenVRRenderWindowInteractorImstk::ClassExitMethodArg = arg;
855 
856  // no call to this->Modified() since this is a class member function
857  }
858 }
859 
860 //------------------------------------------------------------------------------
861 // Set the arg delete method. This is used to free user memory.
862 void
863 vtkOpenVRRenderWindowInteractorImstk::SetClassExitMethodArgDelete(void (* f)(void*))
864 {
865  if (f != vtkOpenVRRenderWindowInteractorImstk::ClassExitMethodArgDelete)
866  {
867  vtkOpenVRRenderWindowInteractorImstk::ClassExitMethodArgDelete = f;
868 
869  // no call to this->Modified() since this is a class member function
870  }
871 }
872 
873 //------------------------------------------------------------------------------
874 void
875 vtkOpenVRRenderWindowInteractorImstk::PrintSelf(ostream& os, vtkIndent indent)
876 {
877  this->Superclass::PrintSelf(os, indent);
878  os << indent << "StartedMessageLoop: " << this->StartedMessageLoop << endl;
879 }
880 
881 //------------------------------------------------------------------------------
882 void
884 {
885  if (this->HasObserver(vtkCommand::ExitEvent))
886  {
887  this->InvokeEvent(vtkCommand::ExitEvent, nullptr);
888  }
890  {
891  (*vtkOpenVRRenderWindowInteractorImstk::ClassExitMethod)(
892  vtkOpenVRRenderWindowInteractorImstk::ClassExitMethodArg);
893  }
894 
895  this->TerminateApp();
896 }
897 
898 //------------------------------------------------------------------------------
899 vtkEventDataDevice
900 vtkOpenVRRenderWindowInteractorImstk::GetPointerDevice()
901 {
902  if (this->PointerIndex == 0)
903  {
904  return vtkEventDataDevice::RightController;
905  }
906  if (this->PointerIndex == 1)
907  {
908  return vtkEventDataDevice::LeftController;
909  }
910  return vtkEventDataDevice::Unknown;
911 }
912 
913 //------------------------------------------------------------------------------
914 void
916  vtkMatrix4x4* startingPhysicalToWorldMatrix)
917 {
918  if (!startingPhysicalToWorldMatrix)
919  {
920  return;
921  }
922  startingPhysicalToWorldMatrix->DeepCopy(this->StartingPhysicalToWorldMatrix);
923 }
virtual int InternalCreateTimer(int timerId, int timerType, unsigned long duration)
virtual void SetPhysicalTranslation(vtkCamera *, double, double, double)
static void SetClassExitMethod(void(*f)(void *), void *arg)
void GetStartingPhysicalToWorldMatrix(vtkMatrix4x4 *startingPhysicalToWorldMatrix)
void AddAction(std::string path, vtkCommand::EventIds, bool isAnalog)
virtual void DoOneEvent(vtkOpenVRRenderWindow *renWin, vtkRenderer *ren, bool doRender)