diff --git a/VirtualReality/MRML/CMakeLists.txt b/VirtualReality/MRML/CMakeLists.txt index 112612f..9241827 100644 --- a/VirtualReality/MRML/CMakeLists.txt +++ b/VirtualReality/MRML/CMakeLists.txt @@ -18,6 +18,8 @@ set(${KIT}_SRCS vtkMRML${MODULE_NAME}LayoutNode.h vtk${MODULE_NAME}ViewInteractor.cxx vtk${MODULE_NAME}ViewInteractor.h + vtk${MODULE_NAME}ViewInteractorObserver.cxx + vtk${MODULE_NAME}ViewInteractorObserver.h vtk${MODULE_NAME}ViewInteractorStyle.cxx vtk${MODULE_NAME}ViewInteractorStyle.h ) diff --git a/VirtualReality/MRML/vtkVirtualRealityViewInteractor.cxx b/VirtualReality/MRML/vtkVirtualRealityViewInteractor.cxx index 2df973f..f8d0e62 100644 --- a/VirtualReality/MRML/vtkVirtualRealityViewInteractor.cxx +++ b/VirtualReality/MRML/vtkVirtualRealityViewInteractor.cxx @@ -195,18 +195,21 @@ void vtkVirtualRealityViewInteractor::SetTriggerButtonFunction(std::string funct return; } + // The "eventId to state" mapping (see call to `MapInputToAction` below) applies to right and left + // controller buttons because they are bound to the same eventId: + // - `vtk_openvr_binding_*.json` files define the "button to action" mapping + // - `vtkOpenVRInteractorStyle()` contructor defines the "action to eventId" mapping + if (functionId.empty()) { - vrInteractorStyle->MapInputToAction(vtkEventDataDevice::RightController, vtkEventDataDeviceInput::Trigger, VTKIS_NONE); - vrInteractorStyle->MapInputToAction(vtkEventDataDevice::LeftController, vtkEventDataDeviceInput::Trigger, VTKIS_NONE); + vrInteractorStyle->MapInputToAction(vtkCommand::Select3DEvent, VTKIS_NONE); this->GestureEnabledButtons.clear(); this->GestureEnabledButtons.push_back(static_cast(vtkEventDataDeviceInput::Grip)); } else if (!functionId.compare(vtkVirtualRealityViewInteractor::GetButtonFunctionIdForGrabObjectsAndWorld())) { - vrInteractorStyle->MapInputToAction(vtkEventDataDevice::RightController, vtkEventDataDeviceInput::Trigger, VTKIS_POSITION_PROP); - vrInteractorStyle->MapInputToAction(vtkEventDataDevice::LeftController, vtkEventDataDeviceInput::Trigger, VTKIS_POSITION_PROP); + vrInteractorStyle->MapInputToAction(vtkCommand::Select3DEvent, VTKIS_POSITION_PROP); this->GestureEnabledButtons.clear(); this->GestureEnabledButtons.push_back(static_cast(vtkEventDataDeviceInput::Grip)); @@ -227,10 +230,14 @@ void vtkVirtualRealityViewInteractor::SetGestureButtonToTrigger() vtkWarningMacro("SetGestureButtonToTrigger: Current interactor style is not a VR interactor style"); return; } - vrInteractorStyle->MapInputToAction(vtkEventDataDevice::RightController, vtkEventDataDeviceInput::Trigger, VTKIS_POSITION_PROP); - vrInteractorStyle->MapInputToAction(vtkEventDataDevice::LeftController, vtkEventDataDeviceInput::Trigger, VTKIS_POSITION_PROP); - vrInteractorStyle->MapInputToAction(vtkEventDataDevice::RightController, vtkEventDataDeviceInput::Grip, VTKIS_NONE); - vrInteractorStyle->MapInputToAction(vtkEventDataDevice::LeftController, vtkEventDataDeviceInput::Grip, VTKIS_NONE); + + // The update "action to (eventId|function)" mapping (see call to `AddAction` below) applies to + // right and left controller buttons because they are bound to the same eventId: + // - "vtk_openvr_binding_*.json" defines the "button -> action" mapping + // - vtkOpenVRInteractorStyle defines the "action -> eventId" mapping + this->AddAction("/actions/vtk/in/TriggerAction", /*isAnalog=*/false, + [this](vtkEventData* ed) { this->HandleComplexGestureEvents(ed); }); + this->AddAction("/actions/vtk/in/ComplexGestureAction", vtkCommand::Select3DEvent, /*isAnalog=*/false); this->GestureEnabledButtons.clear(); this->GestureEnabledButtons.push_back(static_cast(vtkEventDataDeviceInput::Trigger)); @@ -246,10 +253,13 @@ void vtkVirtualRealityViewInteractor::SetGestureButtonToGrip() return; } - vrInteractorStyle->MapInputToAction(vtkEventDataDevice::RightController, vtkEventDataDeviceInput::Trigger, VTKIS_NONE); - vrInteractorStyle->MapInputToAction(vtkEventDataDevice::LeftController, vtkEventDataDeviceInput::Trigger, VTKIS_NONE); - vrInteractorStyle->MapInputToAction(vtkEventDataDevice::RightController, vtkEventDataDeviceInput::Grip, VTKIS_POSITION_PROP); - vrInteractorStyle->MapInputToAction(vtkEventDataDevice::LeftController, vtkEventDataDeviceInput::Grip, VTKIS_POSITION_PROP); + // The update "action to (eventId|function)" mapping (see call to `AddAction` below) applies to + // right and left controller buttons because they are bound to the same eventId: + // - "vtk_openvr_binding_*.json" defines the "button -> action" mapping + // - vtkOpenVRInteractorStyle defines the "action -> eventId" mapping + this->AddAction("/actions/vtk/in/TriggerAction", vtkCommand::Select3DEvent, /*isAnalog=*/false); + this->AddAction("/actions/vtk/in/ComplexGestureAction", /*isAnalog=*/false, + [this](vtkEventData* ed) { this->HandleComplexGestureEvents(ed); }); this->GestureEnabledButtons.clear(); this->GestureEnabledButtons.push_back(static_cast(vtkEventDataDeviceInput::Grip)); @@ -265,10 +275,14 @@ void vtkVirtualRealityViewInteractor::SetGestureButtonToTriggerAndGrip() return; } - vrInteractorStyle->MapInputToAction(vtkEventDataDevice::RightController, vtkEventDataDeviceInput::Trigger, VTKIS_POSITION_PROP); - vrInteractorStyle->MapInputToAction(vtkEventDataDevice::LeftController, vtkEventDataDeviceInput::Trigger, VTKIS_POSITION_PROP); - vrInteractorStyle->MapInputToAction(vtkEventDataDevice::RightController, vtkEventDataDeviceInput::Grip, VTKIS_POSITION_PROP); - vrInteractorStyle->MapInputToAction(vtkEventDataDevice::LeftController, vtkEventDataDeviceInput::Grip, VTKIS_POSITION_PROP); + // The update "action to (eventId|function)" mapping (see call to `AddAction` below) applies to + // right and left controller buttons because they are bound to the same eventId: + // - "vtk_openvr_binding_*.json" defines the "button -> action" mapping + // - vtkOpenVRInteractorStyle defines the "action -> eventId" mapping + this->AddAction("/actions/vtk/in/TriggerAction", /*isAnalog=*/false, + [this](vtkEventData* ed) { this->HandleComplexGestureEvents(ed); }); + this->AddAction("/actions/vtk/in/ComplexGestureAction", /*isAnalog=*/false, + [this](vtkEventData* ed) { this->HandleComplexGestureEvents(ed); }); this->GestureEnabledButtons.clear(); this->GestureEnabledButtons.push_back(static_cast(vtkEventDataDeviceInput::Grip)); @@ -285,10 +299,14 @@ void vtkVirtualRealityViewInteractor::SetGestureButtonToNone() return; } - vrInteractorStyle->MapInputToAction(vtkEventDataDevice::RightController, vtkEventDataDeviceInput::Trigger, VTKIS_NONE); - vrInteractorStyle->MapInputToAction(vtkEventDataDevice::LeftController, vtkEventDataDeviceInput::Trigger, VTKIS_NONE); - vrInteractorStyle->MapInputToAction(vtkEventDataDevice::RightController, vtkEventDataDeviceInput::Grip, VTKIS_NONE); - vrInteractorStyle->MapInputToAction(vtkEventDataDevice::LeftController, vtkEventDataDeviceInput::Grip, VTKIS_NONE); + // The update "action to (eventId|function)" mapping (see call to `AddAction` below) applies to + // right and left controller buttons because they are bound to the same eventId: + // - "vtk_openvr_binding_*.json" defines the "button -> action" mapping + // - vtkOpenVRInteractorStyle defines the "action -> eventId" mapping + this->AddAction("/actions/vtk/in/TriggerAction", /*isAnalog=*/false, + [](vtkEventData* vtkNotUsed(ed)) { }); + this->AddAction("/actions/vtk/in/ComplexGestureAction", /*isAnalog=*/false, + [](vtkEventData* vtkNotUsed(ed)) { }); this->GestureEnabledButtons.clear(); } diff --git a/VirtualReality/MRML/vtkVirtualRealityViewInteractorObserver.cxx b/VirtualReality/MRML/vtkVirtualRealityViewInteractorObserver.cxx new file mode 100644 index 0000000..630398a --- /dev/null +++ b/VirtualReality/MRML/vtkVirtualRealityViewInteractorObserver.cxx @@ -0,0 +1,309 @@ +/*============================================================================== + + Copyright (c) Kitware Inc. + + See COPYRIGHT.txt + or http://www.slicer.org/copyright/copyright.txt for details. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Jean-Christophe Fillion-Robin, Kitware Inc. + and was supported through the University of Western Ontario. + +==============================================================================*/ + +#include "vtkVirtualRealityViewInteractorObserver.h" + +// SlicerVirtualReality includes +#include "vtkVirtualRealityViewInteractorStyle.h" + +// MRML includes +#include "vtkMRMLInteractionEventData.h" + +// VTK includes +#include +#include +#include +#include +#include + +//---------------------------------------------------------------------------- +vtkStandardNewMacro(vtkVirtualRealityViewInteractorObserver); + +//--------------------------------------------------------------------------- +// vtkVirtualRealityViewInteractorObserver methods + +//---------------------------------------------------------------------------- +vtkVirtualRealityViewInteractorObserver::vtkVirtualRealityViewInteractorObserver() +{ + this->EventCallbackCommand->SetCallback(vtkVirtualRealityViewInteractorObserver::CustomProcessEvents); +} + +//---------------------------------------------------------------------------- +vtkVirtualRealityViewInteractorObserver::~vtkVirtualRealityViewInteractorObserver() +{ +} + +//---------------------------------------------------------------------------- +void vtkVirtualRealityViewInteractorObserver::PrintSelf(ostream& os, vtkIndent indent) +{ + this->Superclass::PrintSelf(os,indent); +} + +//---------------------------------------------------------------------------- +void vtkVirtualRealityViewInteractorObserver::SetInteractor(vtkRenderWindowInteractor *interactor) +{ + if (interactor == this->Interactor) + { + return; + } + + this->Superclass::SetInteractor(interactor); + + if (interactor) + { + float priority = 0.0f; + + /// 3D event bindings + // Move3DEvent: Already observed in vtkMRMLViewInteractorStyle + // Button3DEvent: Already observed in vtkMRMLViewInteractorStyle + interactor->AddObserver(vtkCommand::Menu3DEvent, this->EventCallbackCommand, priority); + interactor->AddObserver(vtkCommand::Select3DEvent, this->EventCallbackCommand, priority); + interactor->AddObserver(vtkCommand::NextPose3DEvent, this->EventCallbackCommand, priority); + interactor->AddObserver(vtkCommand::ViewerMovement3DEvent, this->EventCallbackCommand, priority); + interactor->AddObserver(vtkCommand::Pick3DEvent, this->EventCallbackCommand, priority); + interactor->AddObserver(vtkCommand::PositionProp3DEvent, this->EventCallbackCommand, priority); + interactor->AddObserver(vtkCommand::Clip3DEvent, this->EventCallbackCommand, priority); + interactor->AddObserver(vtkCommand::Elevation3DEvent, this->EventCallbackCommand, priority); + + /// Touch gesture interaction events + // Already observed in vtkMRMLViewInteractorStyle + } +} + +//---------------------------------------------------------------------------- +void vtkVirtualRealityViewInteractorObserver::CustomProcessEvents(vtkObject* object, + unsigned long event, void* clientdata, void* calldata) +{ + vtkVirtualRealityViewInteractorObserver* self + = reinterpret_cast(clientdata); + + if (!self->DelegateInteractionEventToDisplayableManagers(event, calldata)) + { + // Displayable managers did not process it + vtkVirtualRealityViewInteractorObserver::ProcessEvents(object, event, clientdata, calldata); + } +} + +//---------------------------------------------------------------------------- +void vtkVirtualRealityViewInteractorObserver::ProcessEvents( + vtkObject* object, unsigned long event, void* clientdata, void* calldata) +{ + vtkVirtualRealityViewInteractorObserver* self = + reinterpret_cast(clientdata); + + switch (event) + { + + case vtkCommand::StartPinchEvent: + case vtkCommand::StartRotateEvent: + case vtkCommand::StartPanEvent: + self->OnStartGesture(); + break; + case vtkCommand::EndPinchEvent: + case vtkCommand::EndRotateEvent: + case vtkCommand::EndPanEvent: + self->OnEndGesture(); + break; + + /// 3D event bindings + // Move3DEvent: Already observed in vtkMRMLViewInteractorStyle + // Button3DEvent: Already observed in vtkMRMLViewInteractorStyle + case vtkCommand::Menu3DEvent: + self->OnMenu3D(static_cast(calldata)); + break; + case vtkCommand::Select3DEvent: + self->OnSelect3D(static_cast(calldata)); + break; + case vtkCommand::NextPose3DEvent: + self->OnNextPose3D(static_cast(calldata)); + break; + case vtkCommand::ViewerMovement3DEvent: + self->OnViewerMovement3D(static_cast(calldata)); + break; + case vtkCommand::Pick3DEvent: + self->OnPick3D(static_cast(calldata)); + break; + case vtkCommand::PositionProp3DEvent: + self->OnPositionProp3D(static_cast(calldata)); + break; + case vtkCommand::Clip3DEvent: + self->OnClip3D(static_cast(calldata)); + break; + case vtkCommand::Elevation3DEvent: + self->OnElevation3D(static_cast(calldata)); + break; + + default: + Superclass::ProcessEvents(object, event, clientdata, calldata); + break; + } +} + + +//---------------------------------------------------------------------------- +// Generic events binding +//---------------------------------------------------------------------------- + +//---------------------------------------------------------------------------- +bool vtkVirtualRealityViewInteractorObserver::DelegateInteractionEventToDisplayableManagers(unsigned long event, void* calldata) +{ + vtkSmartPointer ed; + if (vtkCommand::EventHasData(event)) + { + ed = vtkSmartPointer(static_cast(calldata)); + } + else + { + ed = vtkSmartPointer::New(); + ed->SetType(event); + } + + bool delegated = this->DelegateInteractionEventToDisplayableManagers(ed); + if (delegated) + { + this->EventCallbackCommand->SetAbortFlag(1); + } + return delegated; +} + +//---------------------------------------------------------------------------- +bool vtkVirtualRealityViewInteractorObserver::DelegateInteractionEventToDisplayableManagers(vtkEventData* inputEventData) +{ + // Get display and world position + if (!inputEventData) + { + return false; + } + int* displayPositionInt = this->GetInteractor()->GetEventPosition(); + vtkRenderer* pokedRenderer = this->GetInteractor()->FindPokedRenderer(displayPositionInt[0], displayPositionInt[1]); + if (!pokedRenderer) + { + // can happen during application shutdown + return false; + } + + vtkEventDataDevice3D* inputEventDataDevice3D = inputEventData->GetAsEventDataDevice3D(); + if (!inputEventDataDevice3D) + { + vtkErrorMacro("DelegateInteractionEventToDisplayableManagers: Invalid event data type"); + return false; + } + + return this->Superclass::DelegateInteractionEventToDisplayableManagers(inputEventData); +} + +//---------------------------------------------------------------------------- +bool vtkVirtualRealityViewInteractorObserver::DelegateInteractionEventDataToDisplayableManagers(vtkMRMLInteractionEventData* ed) +{ + + vtkVirtualRealityViewInteractorStyle* vrViewInteractorStyle = + vtkVirtualRealityViewInteractorStyle::SafeDownCast(this->GetInteractorStyle()); + if (vrViewInteractorStyle) + { + ed->SetWorldToPhysicalScale(vrViewInteractorStyle->GetMagnification()); + ed->SetAccuratePicker(vrViewInteractorStyle->GetAccuratePicker()); + } + + vtkRenderer* currentRenderer = this->GetInteractorStyle()->GetCurrentRenderer(); + ed->SetRenderer(currentRenderer); + + std::string interactionContextName; + if (ed->GetDevice() == vtkEventDataDevice::LeftController) + { + interactionContextName = "LeftController"; //TODO: Store these elsewhere + } + else if (ed->GetDevice() == vtkEventDataDevice::RightController) + { + interactionContextName = "RightController"; //TODO: Store these elsewhere + } + else if (ed->GetDevice() == vtkEventDataDevice::HeadMountedDisplay) + { + interactionContextName = "HeadMountedDisplay"; + } + else + { + vtkErrorMacro("DelegateInteractionEventDataToDisplayableManagers: Unrecognized device"); + } + ed->SetInteractionContextName(interactionContextName); + + return this->Superclass::DelegateInteractionEventDataToDisplayableManagers(ed); +} + +//---------------------------------------------------------------------------- +void vtkVirtualRealityViewInteractorObserver::OnStartGesture() +{ + vtkVirtualRealityViewInteractorStyle* vrInteractorStyle = + vtkVirtualRealityViewInteractorStyle::SafeDownCast(this->GetInteractorStyle()); + vrInteractorStyle->OnStartGesture(); +} + +//---------------------------------------------------------------------------- +void vtkVirtualRealityViewInteractorObserver::OnEndGesture() +{ + vtkVirtualRealityViewInteractorStyle* vrInteractorStyle = + vtkVirtualRealityViewInteractorStyle::SafeDownCast(this->GetInteractorStyle()); + vrInteractorStyle->OnEndGesture(); +} + +//---------------------------------------------------------------------------- +void vtkVirtualRealityViewInteractorObserver::OnMenu3D(vtkEventData *edata) +{ + this->GetInteractorStyle()->OnMenu3D(edata); +} + +//---------------------------------------------------------------------------- +void vtkVirtualRealityViewInteractorObserver::OnSelect3D(vtkEventData* edata) +{ + this->GetInteractorStyle()->OnSelect3D(edata); +} + +//------------------------------------------------------------------------------ +void vtkVirtualRealityViewInteractorObserver::OnNextPose3D(vtkEventData *edata) +{ + this->GetInteractorStyle()->OnNextPose3D(edata); +} + +//------------------------------------------------------------------------------ +void vtkVirtualRealityViewInteractorObserver::OnViewerMovement3D(vtkEventData* edata) +{ + this->GetInteractorStyle()->OnViewerMovement3D(edata); +} + +//------------------------------------------------------------------------------ +void vtkVirtualRealityViewInteractorObserver::OnPick3D(vtkEventData *edata) +{ + this->GetInteractorStyle()->OnPick3D(edata); +} + +//------------------------------------------------------------------------------ +void vtkVirtualRealityViewInteractorObserver::OnPositionProp3D(vtkEventData *edata) +{ + this->GetInteractorStyle()->OnPositionProp3D(edata); +} + +//------------------------------------------------------------------------------ +void vtkVirtualRealityViewInteractorObserver::OnClip3D(vtkEventData *edata) +{ + this->GetInteractorStyle()->OnClip3D(edata); +} + +//------------------------------------------------------------------------------ +void vtkVirtualRealityViewInteractorObserver::OnElevation3D(vtkEventData *edata) +{ + this->GetInteractorStyle()->OnElevation3D(edata); +} diff --git a/VirtualReality/MRML/vtkVirtualRealityViewInteractorObserver.h b/VirtualReality/MRML/vtkVirtualRealityViewInteractorObserver.h new file mode 100644 index 0000000..40b307f --- /dev/null +++ b/VirtualReality/MRML/vtkVirtualRealityViewInteractorObserver.h @@ -0,0 +1,96 @@ +/*============================================================================== + + Copyright (c) Kitware Inc. + + See COPYRIGHT.txt + or http://www.slicer.org/copyright/copyright.txt for details. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Jean-Christophe Fillion-Robin, Kitware Inc. + and was supported through the University of Western Ontario. + +==============================================================================*/ + +#ifndef __vtkVirtualRealityViewInteractorObserver_h +#define __vtkVirtualRealityViewInteractorObserver_h + +// MRML includes +#include "vtkMRMLViewInteractorStyle.h" + +#include "vtkSlicerVirtualRealityModuleMRMLExport.h" + +class VTK_SLICER_VIRTUALREALITY_MODULE_MRML_EXPORT vtkVirtualRealityViewInteractorObserver : + public vtkMRMLViewInteractorStyle +{ +public: + static vtkVirtualRealityViewInteractorObserver *New(); + vtkTypeMacro(vtkVirtualRealityViewInteractorObserver,vtkMRMLViewInteractorStyle); + void PrintSelf(ostream& os, vtkIndent indent) override; + + using vtkMRMLViewInteractorStyle::DelegateInteractionEventToDisplayableManagers; + + /// Give a chance to displayable managers to process the event. + /// Based on the return of vtkCommand::EventHasData(event), it either casts the + /// calldata to a vtkEventData or just creates a one of type vtkMRMLInteractionEventData. + /// In both case, it calls DelegateInteractionEventDataToDisplayableManagers + /// passing a vtkEventData object. + /// Return true if the event is processed. + virtual bool DelegateInteractionEventToDisplayableManagers(unsigned long event, void* calldata); + + /// Give a chance to displayable managers to process the event. + /// It creates vtkMRMLInteractionEventData and calls + /// DelegateInteractionEventDataToDisplayableManagers. + /// Return true if the event is processed. + bool DelegateInteractionEventToDisplayableManagers(vtkEventData* inputEventData) override; + + /// Give a chance to displayable managers to process the event. + /// Return true if the event is processed. + using vtkMRMLViewInteractorStyle::DelegateInteractionEventDataToDisplayableManagers; + virtual bool DelegateInteractionEventDataToDisplayableManagers(vtkMRMLInteractionEventData* eventData) override; + + /// Set the Interactor wrapper being controlled by this object. (Satisfy superclass API.) + void SetInteractor(vtkRenderWindowInteractor *interactor) override; + + /// Main process event method. + /// + /// If calling DelegateInteractionEventToDisplayableManagers() returns false or if the current motion flag + /// returned by vtkInteractorStyle::GetState() is VTKIS_NONE, call ProcessEvents(). + static void CustomProcessEvents(vtkObject* object, unsigned long event, void* clientdata, void* calldata); + + /// Process events not already delegated to displayable managers by CustomProcessEvents(). + static void ProcessEvents(vtkObject* object, unsigned long event, void* clientdata, void* calldata); + + /// ComplexGesture bindings + virtual void OnStartGesture(); + virtual void OnEndGesture(); + + /// 3D event bindings + // OnMove3D: Already implemented in vtkMRMLViewInteractorStyle + // OnButton3D: Already implemented in vtkMRMLViewInteractorStyle + virtual void OnMenu3D(vtkEventData *edata); + virtual void OnSelect3D(vtkEventData *edata); + virtual void OnNextPose3D(vtkEventData *edata); + virtual void OnViewerMovement3D(vtkEventData *edata); + virtual void OnPick3D(vtkEventData *edata); + virtual void OnPositionProp3D(vtkEventData *edata); + virtual void OnClip3D(vtkEventData *edata); + virtual void OnElevation3D(vtkEventData *edata); + +protected: + vtkVirtualRealityViewInteractorObserver(); + ~vtkVirtualRealityViewInteractorObserver() override; + +private: + vtkVirtualRealityViewInteractorObserver(const vtkVirtualRealityViewInteractorObserver&) = delete; + void operator=(const vtkVirtualRealityViewInteractorObserver&) = delete; + + class vtkInternal; + vtkInternal* Internal; +}; + +#endif diff --git a/VirtualReality/MRML/vtkVirtualRealityViewInteractorStyle.cxx b/VirtualReality/MRML/vtkVirtualRealityViewInteractorStyle.cxx index 9ae1153..489fb31 100644 --- a/VirtualReality/MRML/vtkVirtualRealityViewInteractorStyle.cxx +++ b/VirtualReality/MRML/vtkVirtualRealityViewInteractorStyle.cxx @@ -25,6 +25,7 @@ // MRML includes #include "vtkMRMLAbstractThreeDViewDisplayableManager.h" +#include "vtkMRMLDisplayableManagerGroup.h" #include "vtkMRMLDisplayableNode.h" #include "vtkMRMLDisplayNode.h" #include "vtkMRMLInteractionEventData.h" @@ -35,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -45,10 +47,12 @@ #include #include +#include #include "vtkOpenVRRenderWindow.h" #include "vtkOpenVRRenderWindowInteractor.h" +//---------------------------------------------------------------------------- vtkStandardNewMacro(vtkVirtualRealityViewInteractorStyle); //---------------------------------------------------------------------------- @@ -65,9 +69,6 @@ class vtkVirtualRealityViewInteractorStyle::vtkInternal vtkMatrix4x4* controller0Pose, vtkMatrix4x4* controller1Pose, vtkMatrix4x4* combinedPose); public: - /// Store required controllers information when performing action - int InteractionState[vtkEventDataNumberOfDevices]; - vtkWeakPointer PickedNode[vtkEventDataNumberOfDevices]; //vtkPlane* ClippingPlanes[vtkEventDataNumberOfDevices]; @@ -86,7 +87,6 @@ vtkVirtualRealityViewInteractorStyle::vtkInternal::vtkInternal() { for (int d = 0; d < vtkEventDataNumberOfDevices; ++d) { - this->InteractionState[d] = VTKIS_NONE; this->PickedNode[d] = nullptr; //this->ClippingPlanes[d] = nullptr; } @@ -175,30 +175,67 @@ vtkVirtualRealityViewInteractorStyle::vtkVirtualRealityViewInteractorStyle() this->Internal = new vtkInternal(); - for (int d = 0; d < vtkEventDataNumberOfDevices; ++d) - { - this->Internal->InteractionState[d] = VTKIS_NONE; - - for (int i = 0; i < vtkEventDataNumberOfInputs; i++) - { - this->InputMap[d][i] = -1; - } - } this->AccuratePicker = vtkSmartPointer::New(); this->AccuratePicker->SetTolerance( .005 ); this->QuickPicker = vtkSmartPointer::New(); // Create default inputs mapping + + // The "eventId to state" mapping (see call to `MapInputToAction` below) applies to right and left + // controller buttons because they are bound to the same eventId: + // - `vtk_openvr_binding_*.json` files define the "button to action" mapping + // - `vtkOpenVRInteractorStyle()` contructor defines the "action to eventId" mapping + + this->MapInputToAction(vtkCommand::ViewerMovement3DEvent, VTKIS_DOLLY); + this->MapInputToAction(vtkCommand::Select3DEvent, VTKIS_POSITION_PROP); + + /* this->MapInputToAction(vtkEventDataDevice::RightController, vtkEventDataDeviceInput::Grip, VTKIS_POSITION_PROP); + this->MapInputToAction(vtkEventDataDevice::RightController, vtkEventDataDeviceInput::TrackPad, VTKIS_DOLLY); this->MapInputToAction(vtkEventDataDevice::LeftController, vtkEventDataDeviceInput::Grip, VTKIS_POSITION_PROP); - - this->EventCallbackCommand->SetCallback(vtkVirtualRealityViewInteractorStyle::ProcessEvents); + */ + + // Before in Slicer: + // vtkEventDataDevice::RightController, vtkEventDataDeviceInput::Grip -> VTKIS_POSITION_PROP + // + // After in Slicer: + // See above + // + // Before OpenVR refactoring to "action based input model" introduced in kitware/VTK@b7f02e622: + // vtkEventDataDevice::RightController vtkEventDataDeviceInput::Trigger -> VTKIS_POSITION_PROP + // + // After OpenVR refactoring: + // vtkCommand::Select3DEvent -> VTKIS_POSITION_PROP + + // Before in Slicer: + // vtkEventDataDevice::RightController, vtkEventDataDeviceInput::TrackPad -> VTKIS_DOLLY + // + // After in Slicer: + // See above + // + // Before OpenVR refactoring to "action based input model" introduced in kitware/VTK@b7f02e622: + // same mapping + // + // After OpenVR refactoring: + // Calling MapInputToAction with "vtkCommand::ViewerMovement3DEvent -> VTKIS_DOLLY" + + // Before in Slicer: + // vtkEventDataDevice::LeftController, vtkEventDataDeviceInput::Grip -> VTKIS_POSITION_PROP + // + // After in Slicer: + // See above + // + // Before OpenVR refactoring to "action based input model" introduced in kitware/VTK@b7f02e622: + // No mapping + // + // After OpenVR refactoring: + // No mapping } //---------------------------------------------------------------------------- @@ -222,150 +259,17 @@ void vtkVirtualRealityViewInteractorStyle::SetInteractor(vtkRenderWindowInteract } this->Superclass::SetInteractor(i); - - if (i) - { - this->SetupActions(i); - } - - // add observers for each of the events handled in ProcessEvents - if (i) - { - i->AddObserver(vtkCommand::StartPinchEvent, - this->EventCallbackCommand, - this->Priority); - - i->AddObserver(vtkCommand::StartRotateEvent, - this->EventCallbackCommand, - this->Priority); - - i->AddObserver(vtkCommand::StartPanEvent, - this->EventCallbackCommand, - this->Priority); - - i->AddObserver(vtkCommand::EndPinchEvent, - this->EventCallbackCommand, - this->Priority); - - i->AddObserver(vtkCommand::EndRotateEvent, - this->EventCallbackCommand, - this->Priority); - - i->AddObserver(vtkCommand::EndPanEvent, - this->EventCallbackCommand, - this->Priority); - } -} - -//---------------------------------------------------------------------------- -void vtkVirtualRealityViewInteractorStyle::ProcessEvents( - vtkObject* object, unsigned long event, void* clientdata, void* calldata) -{ - Superclass::ProcessEvents(object, event, clientdata, calldata); - - vtkVirtualRealityViewInteractorStyle* self = - reinterpret_cast(clientdata); - - switch (event) - { - case vtkCommand::StartPinchEvent: - case vtkCommand::StartRotateEvent: - case vtkCommand::StartPanEvent: - self->OnStartGesture(); - break; - case vtkCommand::EndPinchEvent: - case vtkCommand::EndRotateEvent: - case vtkCommand::EndPanEvent: - self->OnEndGesture(); - break; - } } //---------------------------------------------------------------------------- -// Copied from vtkOpenVRInteractorStyle::SetupActions -//------------------------------------------------------------------------------ -void vtkVirtualRealityViewInteractorStyle::SetupActions(vtkRenderWindowInteractor* iren) +void vtkVirtualRealityViewInteractorStyle::SetDisplayableManagers(vtkMRMLDisplayableManagerGroup* displayableManagers) { - vtkOpenVRRenderWindowInteractor* oiren = vtkOpenVRRenderWindowInteractor::SafeDownCast(iren); - - if (oiren) - { - oiren->AddAction("/actions/vtk/in/Elevation", vtkCommand::Elevation3DEvent, true); - oiren->AddAction("/actions/vtk/in/Movement", vtkCommand::ViewerMovement3DEvent, true); - oiren->AddAction("/actions/vtk/in/NextCameraPose", vtkCommand::NextPose3DEvent, false); - oiren->AddAction("/actions/vtk/in/PositionProp", vtkCommand::PositionProp3DEvent, false); - oiren->AddAction("/actions/vtk/in/ShowMenu", vtkCommand::Menu3DEvent, false); - oiren->AddAction("/actions/vtk/in/StartElevation", vtkCommand::Elevation3DEvent, false); - oiren->AddAction("/actions/vtk/in/StartMovement", vtkCommand::ViewerMovement3DEvent, false); - oiren->AddAction("/actions/vtk/in/TriggerAction", vtkCommand::Select3DEvent, false); - } -} - -//---------------------------------------------------------------------------- -// Generic events binding -//---------------------------------------------------------------------------- - -//---------------------------------------------------------------------------- -bool vtkVirtualRealityViewInteractorStyle::DelegateInteractionEventToDisplayableManagers(vtkEventData* inputEventData) -{ - // Get display and world position - int* displayPositionInt = this->GetInteractor()->GetEventPosition(); - vtkRenderer* pokedRenderer = this->GetInteractor()->FindPokedRenderer(displayPositionInt[0], displayPositionInt[1]); - if (!pokedRenderer || !inputEventData) - { - // can happen during application shutdown - return false; - } - - vtkNew ed; - ed->SetType(inputEventData->GetType()); - int displayPositionCorrected[2] = { displayPositionInt[0] - pokedRenderer->GetOrigin()[0], displayPositionInt[1] - pokedRenderer->GetOrigin()[1] }; - ed->SetDisplayPosition(displayPositionCorrected); - double worldPosition[3] = { 0.0, 0.0, 0.0 }; - vtkEventDataDevice3D* inputEventDataDevice3D = inputEventData->GetAsEventDataDevice3D(); - if (!inputEventDataDevice3D) - { - vtkErrorMacro("DelegateInteractionEventToDisplayableManagers: Invalid event data type"); - return false; - } - inputEventDataDevice3D->GetWorldPosition(worldPosition); - ed->SetDevice(inputEventDataDevice3D->GetDevice()); - ed->SetWorldPosition(worldPosition, true); - ed->SetWorldToPhysicalScale(this->GetMagnification()); - ed->SetAccuratePicker(this->AccuratePicker); - ed->SetRenderer(this->CurrentRenderer); - std::string interactionContextName; - if (ed->GetDevice() == vtkEventDataDevice::LeftController) - { - interactionContextName = "LeftController"; //TODO: Store these elsewhere - } - else if (ed->GetDevice() == vtkEventDataDevice::RightController) - { - interactionContextName = "RightController"; //TODO: Store these elsewhere - } - else if (ed->GetDevice() == vtkEventDataDevice::HeadMountedDisplay) - { - interactionContextName = "HeadMountedDisplay"; - } - else - { - vtkErrorMacro("DelegateInteractionEventToDisplayableManagers: Unrecognized device"); - } - ed->SetInteractionContextName(interactionContextName); - - ed->SetAttributesFromInteractor(this->GetInteractor()); - - return this->DelegateInteractionEventDataToDisplayableManagers(ed); + this->DisplayableManagers = displayableManagers; } //---------------------------------------------------------------------------- void vtkVirtualRealityViewInteractorStyle::OnMove3D(vtkEventData* edata) { - if (this->DelegateInteractionEventToDisplayableManagers(edata)) - { - return; - } - vtkEventDataDevice3D *edd = edata->GetAsEventDataDevice3D(); if (!edd) { @@ -377,7 +281,7 @@ void vtkVirtualRealityViewInteractorStyle::OnMove3D(vtkEventData* edata) int x = this->Interactor->GetEventPosition()[0]; int y = this->Interactor->GetEventPosition()[1]; - switch (this->Internal->InteractionState[idev]) + switch (this->InteractionState[idev]) { case VTKIS_POSITION_PROP: this->FindPokedRenderer(x, y); @@ -400,20 +304,12 @@ void vtkVirtualRealityViewInteractorStyle::OnMove3D(vtkEventData* edata) } //// Update rays - //if (this->HoverPick) - //{ - // this->UpdateRay(edd->GetDevice()); - //} + this->UpdateRay(edd->GetDevice()); } //---------------------------------------------------------------------------- void vtkVirtualRealityViewInteractorStyle::OnSelect3D(vtkEventData* edata) { - if (this->DelegateInteractionEventToDisplayableManagers(edata)) - { - return; - } - vtkEventDataDevice3D *bd = edata->GetAsEventDataDevice3D(); if (!bd) { @@ -424,8 +320,9 @@ void vtkVirtualRealityViewInteractorStyle::OnSelect3D(vtkEventData* edata) int y = this->Interactor->GetEventPosition()[1]; this->FindPokedRenderer(x, y); - int state = this->InputMap[static_cast(bd->GetDevice())][static_cast(bd->GetInput())]; - if (state == -1) + int eid = bd->GetType(); + int state = this->GetMappedAction(static_cast(eid)); + if (state == VTKIS_NONE) { return; } @@ -449,7 +346,6 @@ void vtkVirtualRealityViewInteractorStyle::OnViewerMovement3D(vtkEventData* edat // were added in VTK@7a2c71e and VTK@ca4441c } - //------------------------------------------------------------------------------ void vtkVirtualRealityViewInteractorStyle::Movement3D(int interactionState, vtkEventData* edata) { @@ -490,7 +386,7 @@ void vtkVirtualRealityViewInteractorStyle::Movement3D(int interactionState, vtkE // call start. When the joystick returns to the center, call end. if ((edd->GetInput() == vtkEventDataDeviceInput::Joystick || edd->GetInput() == vtkEventDataDeviceInput::TrackPad) && - this->Internal->InteractionState[idev] != interactionState && fabs(pos[1]) > 0.1) + this->InteractionState[idev] != interactionState && fabs(pos[1]) > 0.1) { this->StartAction(interactionState, edd); this->LastTrackPadPosition[0] = 0.0; @@ -498,7 +394,7 @@ void vtkVirtualRealityViewInteractorStyle::Movement3D(int interactionState, vtkE return; } - if (this->Internal->InteractionState[idev] == interactionState) + if (this->InteractionState[idev] == interactionState) { // Stop when returning to the center on the joystick if ((edd->GetInput() == vtkEventDataDeviceInput::Joystick || @@ -687,7 +583,7 @@ void vtkVirtualRealityViewInteractorStyle::StartPositionProp(vtkEventDataDevice3 } } - this->Internal->InteractionState[deviceIndex] = VTKIS_POSITION_PROP; + this->InteractionState[deviceIndex] = VTKIS_POSITION_PROP; // Don't start action if a controller is already positioning the prop int rc = static_cast(vtkEventDataDevice::RightController); @@ -708,7 +604,7 @@ void vtkVirtualRealityViewInteractorStyle::StartPositionProp(vtkEventDataDevice3 void vtkVirtualRealityViewInteractorStyle::EndPositionProp(vtkEventDataDevice3D* edata) { int deviceIndex = static_cast(edata->GetDevice()); - this->Internal->InteractionState[deviceIndex] = VTKIS_NONE; + this->InteractionState[deviceIndex] = VTKIS_NONE; this->Internal->PickedNode[deviceIndex] = nullptr; } @@ -720,7 +616,7 @@ void vtkVirtualRealityViewInteractorStyle::StartDolly3D(vtkEventDataDevice3D* ed return; } vtkEventDataDevice dev = ed->GetDevice(); - this->Internal->InteractionState[static_cast(dev)] = VTKIS_DOLLY; + this->InteractionState[static_cast(dev)] = VTKIS_DOLLY; this->LastDolly3DEventTime->StartTimer(); // this->GrabFocus(this->EventCallbackCommand); @@ -730,7 +626,7 @@ void vtkVirtualRealityViewInteractorStyle::StartDolly3D(vtkEventDataDevice3D* ed void vtkVirtualRealityViewInteractorStyle::EndDolly3D(vtkEventDataDevice3D* ed) { vtkEventDataDevice dev = ed->GetDevice(); - this->Internal->InteractionState[static_cast(dev)] = VTKIS_NONE; + this->InteractionState[static_cast(dev)] = VTKIS_NONE; this->LastDolly3DEventTime->StopTimer(); } @@ -769,8 +665,8 @@ void vtkVirtualRealityViewInteractorStyle::OnPinch3D() return; } - this->Internal->InteractionState[rc] = VTKIS_ZOOM; - this->Internal->InteractionState[lc] = VTKIS_ZOOM; + this->InteractionState[rc] = VTKIS_ZOOM; + this->InteractionState[lc] = VTKIS_ZOOM; if (this->CurrentRenderer == nullptr) { @@ -886,34 +782,24 @@ void vtkVirtualRealityViewInteractorStyle::OnEndGesture() //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- -void vtkVirtualRealityViewInteractorStyle::MapInputToAction( - vtkEventDataDevice device, vtkEventDataDeviceInput input, int state) +int vtkVirtualRealityViewInteractorStyle::GetMappedAction(vtkCommand::EventIds eid) { - if (input >= vtkEventDataDeviceInput::NumberOfInputs || state < VTKIS_NONE) - { - return; - } - - int oldstate = this->InputMap[static_cast(device)][static_cast(input)]; - if (oldstate == state) - { - return; - } + // Since both `vtkEventDataAction::Press` and `vtkEventDataAction::Release` actions are + // added together to the map when using the 2-argument function + // `MapInputToAction(vtkCommand::EventIds eid, int state)`, and we are only using this + // version of the function, to get the state for the corresponding `(eventId, action)`, + // we simply lookup for `(eventId, vtkEventDataAction::Press)` to check if an event + // is already mapped. + vtkEventDataAction action = vtkEventDataAction::Press; - this->InputMap[static_cast(device)][static_cast(input)] = state; - //this->AddTooltipForInput(device, input); //TODO: + decltype(this->InputMap)::key_type key(eid, action); - this->Modified(); -} - -//---------------------------------------------------------------------------- -int vtkVirtualRealityViewInteractorStyle::GetMappedAction(vtkEventDataDevice device, vtkEventDataDeviceInput input) -{ - if (input >= vtkEventDataDeviceInput::NumberOfInputs || device >= vtkEventDataDevice::NumberOfDevices ) + auto it = this->InputMap.find(key); + if (it != this->InputMap.end()) { - return VTKIS_NONE; + return it->second; } - return this->InputMap[static_cast(device)][static_cast(input)]; + return VTKIS_NONE; } //---------------------------------------------------------------------------- @@ -1000,6 +886,12 @@ vtkMRMLScene* vtkVirtualRealityViewInteractorStyle::GetMRMLScene() return this->DisplayableManagers->GetNthDisplayableManager(0)->GetMRMLScene(); } +//--------------------------------------------------------------------------- +vtkCellPicker* vtkVirtualRealityViewInteractorStyle::GetAccuratePicker() +{ + return this->AccuratePicker; +} + //--------------------------------------------------------------------------- void vtkVirtualRealityViewInteractorStyle::SetMagnification(double magnification) { diff --git a/VirtualReality/MRML/vtkVirtualRealityViewInteractorStyle.h b/VirtualReality/MRML/vtkVirtualRealityViewInteractorStyle.h index c174b02..8c2ab60 100644 --- a/VirtualReality/MRML/vtkVirtualRealityViewInteractorStyle.h +++ b/VirtualReality/MRML/vtkVirtualRealityViewInteractorStyle.h @@ -22,16 +22,19 @@ #define __vtkVirtualRealityViewInteractorStyle_h // MRML includes -#include "vtkMRMLDisplayableManagerGroup.h" #include "vtkMRMLViewInteractorStyle.h" // VTK includes +#include +#include #include "vtkObject.h" #include "vtkEventData.h" +#include #include "vtkSlicerVirtualRealityModuleMRMLExport.h" class vtkMRMLScene; +class vtkMRMLDisplayableManagerGroup; class vtkCellPicker; class vtkWorldPointPicker; @@ -40,28 +43,17 @@ class vtkWorldPointPicker; /// TODO: /// class VTK_SLICER_VIRTUALREALITY_MODULE_MRML_EXPORT vtkVirtualRealityViewInteractorStyle : - public vtkMRMLViewInteractorStyle + public vtkOpenVRInteractorStyle { public: static vtkVirtualRealityViewInteractorStyle *New(); - vtkTypeMacro(vtkVirtualRealityViewInteractorStyle,vtkMRMLViewInteractorStyle); + vtkTypeMacro(vtkVirtualRealityViewInteractorStyle,vtkOpenVRInteractorStyle); void PrintSelf(ostream& os, vtkIndent indent) override; - - /// Give a chance to displayable managers to process the event. - /// Return true if the event is processed. - using vtkMRMLViewInteractorStyle::DelegateInteractionEventToDisplayableManagers; - bool DelegateInteractionEventToDisplayableManagers(vtkEventData* inputEventData) override; /// Set the Interactor wrapper being controlled by this object. (Satisfy superclass API.) void SetInteractor(vtkRenderWindowInteractor *interactor) override; - /// Main process event method - static void ProcessEvents(vtkObject* object, unsigned long event, void* clientdata, void* calldata); - - /** - * Setup default actions defined with an action path and a corresponding command. - */ - void SetupActions(vtkRenderWindowInteractor* iren); + void SetDisplayableManagers(vtkMRMLDisplayableManagerGroup* displayableManagers); /// Get MRML scene from the displayable manager group (the first displayable manager's if any) vtkMRMLScene* GetMRMLScene(); @@ -77,14 +69,8 @@ class VTK_SLICER_VIRTUALREALITY_MODULE_MRML_EXPORT vtkVirtualRealityViewInteract /** * Interaction mode entry points. */ - //virtual void StartPick(vtkEventDataDevice3D *); - //virtual void EndPick(vtkEventDataDevice3D *); - //virtual void StartLoadCamPose(vtkEventDataDevice3D *); - //virtual void EndLoadCamPose(vtkEventDataDevice3D *); virtual void StartPositionProp(vtkEventDataDevice3D *); virtual void EndPositionProp(vtkEventDataDevice3D *); - //virtual void StartClip(vtkEventDataDevice3D *); - //virtual void EndClip(vtkEventDataDevice3D *); virtual void StartDolly3D(vtkEventDataDevice3D *); virtual void EndDolly3D(vtkEventDataDevice3D *); //@} @@ -105,7 +91,6 @@ class VTK_SLICER_VIRTUALREALITY_MODULE_MRML_EXPORT vtkVirtualRealityViewInteract /** * Methods for interaction. */ - //void LoadNextCameraPose(); void PositionProp(vtkEventData *, double* lwpos = nullptr, double* lwori = nullptr) override; //@} @@ -115,45 +100,18 @@ class VTK_SLICER_VIRTUALREALITY_MODULE_MRML_EXPORT vtkVirtualRealityViewInteract * Actions are defined by a VTKIS_*STATE*, interaction entry points, * and the corresponding method for interaction. */ - void MapInputToAction(vtkEventDataDevice device, - vtkEventDataDeviceInput input, int state); - int GetMappedAction(vtkEventDataDevice device, vtkEventDataDeviceInput input); + int GetMappedAction(vtkCommand::EventIds eid); //@} - ////@{ - ///** - //* Define the helper text that goes with an input - //*/ - //void AddTooltipForInput(vtkEventDataDevice device, - // vtkEventDataDeviceInput input, const std::string &text); - ////@} - - //vtkSetClampMacro(HoverPick, int, 0, 1); - //vtkGetMacro(HoverPick, int); - //vtkBooleanMacro(HoverPick, int); - vtkSetClampMacro(GrabEnabled, int, 0, 1); vtkGetMacro(GrabEnabled, int); vtkBooleanMacro(GrabEnabled, int); - //int GetInteractionState(vtkEventDataDevice device) { - // return this->InteractionState[static_cast(device)]; } - - //void ShowRay(vtkEventDataDevice controller); - //void HideRay(vtkEventDataDevice controller); - - //void ShowBillboard(const std::string &text); - //void HideBillboard(); - - //void ShowPickSphere(double *pos, double radius, vtkProp3D *); - //void ShowPickCell(vtkCell *cell, vtkProp3D *); - //void HidePickActor(); - - //void ToggleDrawControls(); - //// allow the user to add options to the menu //vtkOpenVRMenuWidget *GetMenu() { // return this->Menu.Get(); } + + vtkCellPicker* GetAccuratePicker(); /// Set physical to world magnification. Valid value range is [0.01, 100]. /// Note: Conversion is physicalScale = 1000 / magnification @@ -161,47 +119,14 @@ class VTK_SLICER_VIRTUALREALITY_MODULE_MRML_EXPORT vtkVirtualRealityViewInteract double GetMagnification(); protected: - //void EndPickCallback(vtkSelection *sel); - - ////Ray drawing - //void UpdateRay(vtkEventDataDevice controller); - - //vtkNew Menu; - //vtkNew MenuRepresentation; - //vtkCallbackCommand* MenuCommand; - //static void MenuCallback(vtkObject* object, - // unsigned long event, - // void* clientdata, - // void* calldata); - - //vtkNew TextActor3D; - //vtkNew PickActor; - //vtkNew Sphere; - - // Device input to interaction state mapping - int InputMap[vtkEventDataNumberOfDevices][vtkEventDataNumberOfInputs]; - //vtkOpenVRControlsHelper* ControlsHelpers[vtkEventDataNumberOfDevices][vtkEventDataNumberOfInputs]; // Utility routines void StartAction(int VTKIS_STATE, vtkEventDataDevice3D *edata); void EndAction(int VTKIS_STATE, vtkEventDataDevice3D *edata); - ///** - //* Controls helpers drawing - //*/ - //void AddTooltipForInput(vtkEventDataDevice device, vtkEventDataDeviceInput input); - bool QuickPick(int x, int y, double pickPoint[3]); protected: - ///** - //* Indicates if picking should be updated every frame. If so, the interaction - //* picker will try to pick a prop and rays will be updated accordingly. - //* Default is set to off. - //*/ - //int HoverPick; - - //vtkNew HardwarePicker; /// For jump to slice feature (when mouse is moved while shift key is pressed) vtkSmartPointer AccuratePicker; @@ -218,6 +143,8 @@ class VTK_SLICER_VIRTUALREALITY_MODULE_MRML_EXPORT vtkVirtualRealityViewInteract */ void Movement3D(int interactionState, vtkEventData* edata); + vtkWeakPointer DisplayableManagers; + private: vtkVirtualRealityViewInteractorStyle(const vtkVirtualRealityViewInteractorStyle&); /// Not implemented. void operator=(const vtkVirtualRealityViewInteractorStyle&); /// Not implemented. diff --git a/VirtualReality/Widgets/qMRMLVirtualRealityView.cxx b/VirtualReality/Widgets/qMRMLVirtualRealityView.cxx index e56d6d3..cb37f74 100644 --- a/VirtualReality/Widgets/qMRMLVirtualRealityView.cxx +++ b/VirtualReality/Widgets/qMRMLVirtualRealityView.cxx @@ -20,6 +20,7 @@ // Need to be included before qMRMLVRView_p #include +#include #include //#include //TODO: For debugging the original interactor #include @@ -163,6 +164,10 @@ void qMRMLVirtualRealityViewPrivate::createRenderWindow() this->Interactor->SetInteractorStyle(this->InteractorStyle); this->InteractorStyle->SetInteractor(this->Interactor); this->InteractorStyle->SetCurrentRenderer(this->Renderer); + + this->InteractorObserver = vtkVirtualRealityViewInteractorObserver::New(); + this->InteractorObserver->SetInteractor(this->Interactor); + this->Camera = vtkSmartPointer::New(); this->Renderer->SetActiveCamera(this->Camera); @@ -216,6 +221,7 @@ void qMRMLVirtualRealityViewPrivate::createRenderWindow() factory->InstantiateDisplayableManagers(q->renderer())); this->DisplayableManagerGroup->SetMRMLDisplayableNode(this->MRMLVirtualRealityViewNode); this->InteractorStyle->SetDisplayableManagers(this->DisplayableManagerGroup); + this->InteractorObserver->SetDisplayableManagers(this->DisplayableManagerGroup); qDebug() << Q_FUNC_INFO << ": Number of registered displayable manager:" << this->DisplayableManagerGroup->GetDisplayableManagerCount(); @@ -681,6 +687,13 @@ qMRMLVirtualRealityView::~qMRMLVirtualRealityView() { } +//------------------------------------------------------------------------------ +vtkVirtualRealityViewInteractorObserver* qMRMLVirtualRealityView::interactorObserver()const +{ + Q_D(const qMRMLVirtualRealityView); + return d->InteractorObserver; +} + //------------------------------------------------------------------------------ void qMRMLVirtualRealityView::addDisplayableManager(const QString& displayableManagerName) { @@ -777,13 +790,19 @@ bool qMRMLVirtualRealityView::isGrabObjectsEnabled() void qMRMLVirtualRealityView::setDolly3DEnabled(bool enable) { Q_D(qMRMLVirtualRealityView); + + // The "eventId to state" mapping (see call to `MapInputToAction` below) applies to right and left + // controller buttons because they are bound to the same eventId: + // - `vtk_openvr_binding_*.json` files define the "button to action" mapping + // - `vtkOpenVRInteractorStyle()` contructor defines the "action to eventId" mapping + if (enable) { - d->InteractorStyle->MapInputToAction(vtkEventDataDevice::RightController, vtkEventDataDeviceInput::TrackPad, VTKIS_DOLLY); + d->InteractorStyle->MapInputToAction(vtkCommand::ViewerMovement3DEvent, VTKIS_DOLLY); } else { - d->InteractorStyle->MapInputToAction(vtkEventDataDevice::RightController, vtkEventDataDeviceInput::TrackPad, VTKIS_NONE); + d->InteractorStyle->MapInputToAction(vtkCommand::ViewerMovement3DEvent, VTKIS_NONE); } } @@ -792,7 +811,7 @@ bool qMRMLVirtualRealityView::isDolly3DEnabled() { Q_D(qMRMLVirtualRealityView); - return d->InteractorStyle->GetMappedAction(vtkEventDataDevice::RightController, vtkEventDataDeviceInput::TrackPad) == VTKIS_DOLLY; + return d->InteractorStyle->GetMappedAction(vtkCommand::ViewerMovement3DEvent) == VTKIS_DOLLY; } //------------------------------------------------------------------------------ diff --git a/VirtualReality/Widgets/qMRMLVirtualRealityView.h b/VirtualReality/Widgets/qMRMLVirtualRealityView.h index 573e923..9b4f57b 100644 --- a/VirtualReality/Widgets/qMRMLVirtualRealityView.h +++ b/VirtualReality/Widgets/qMRMLVirtualRealityView.h @@ -39,6 +39,7 @@ class vtkCollection; class vtkGenericOpenGLRenderWindow; class vtkRenderWindowInteractor; class vtkSlicerCamerasModuleLogic; +class vtkVirtualRealityViewInteractorObserver; class vtkOpenVRRenderer; class vtkOpenVRRenderWindow; @@ -64,6 +65,9 @@ class Q_SLICER_QTMODULES_VIRTUALREALITY_WIDGETS_EXPORT qMRMLVirtualRealityView : explicit qMRMLVirtualRealityView(QWidget* parent = nullptr); virtual ~qMRMLVirtualRealityView(); + /// Returns the interactor observer of the view + Q_INVOKABLE vtkVirtualRealityViewInteractorObserver* interactorObserver()const; + /// Add a displayable manager to the view, /// the displayable manager is proper to the 3D view and is not shared /// with other views. diff --git a/VirtualReality/Widgets/qMRMLVirtualRealityView_p.h b/VirtualReality/Widgets/qMRMLVirtualRealityView_p.h index 1900fd1..d2a075f 100644 --- a/VirtualReality/Widgets/qMRMLVirtualRealityView_p.h +++ b/VirtualReality/Widgets/qMRMLVirtualRealityView_p.h @@ -57,6 +57,7 @@ class vtkOpenVRInteractorStyle; class vtkOpenVRRenderWindowInteractor; class vtkTimerLog; class vtkVirtualRealityViewInteractor; +class vtkVirtualRealityViewInteractorObserver; class vtkVirtualRealityViewInteractorStyle; namespace vr @@ -99,6 +100,7 @@ public slots: vtkSlicerCamerasModuleLogic* CamerasLogic; vtkSmartPointer DisplayableManagerGroup; + vtkSmartPointer InteractorObserver; vtkWeakPointer MRMLVirtualRealityViewNode; vtkSmartPointer Renderer; vtkSmartPointer RenderWindow;