1 // Copyright (c) 2020 OPEN CASCADE SAS
3 // This file is part of Open CASCADE Technology software library.
5 // This library is free software; you can redistribute it and/or modify it under
6 // the terms of the GNU Lesser General Public License version 2.1 as published
7 // by the Free Software Foundation, with special exception defined in the file
8 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
9 // distribution for complete text of the license and disclaimer of any warranty.
11 // Alternatively, this file may be used under the terms of Open CASCADE
12 // commercial license or contractual agreement.
14 #include <Aspect_OpenVRSession.hxx>
16 #include <Graphic3d_ArrayOfTriangles.hxx>
17 #include <Image_PixMap.hxx>
18 #include <Image_Texture.hxx>
19 #include <Message.hxx>
20 #include <Message_Messenger.hxx>
21 #include <NCollection_LocalArray.hxx>
23 #include <OSD_Environment.hxx>
24 #include <OSD_File.hxx>
25 #include <OSD_Process.hxx>
32 //! Print OpenVR compositor error.
33 static const char* getVRCompositorError (vr::EVRCompositorError theVRError)
37 case vr::VRCompositorError_None: return "None";
38 case vr::VRCompositorError_RequestFailed: return "Request Failed";
39 case vr::VRCompositorError_IncompatibleVersion: return "Incompatible Version";
40 case vr::VRCompositorError_DoNotHaveFocus: return "Do not have focus";
41 case vr::VRCompositorError_InvalidTexture: return "Invalid Texture";
42 case vr::VRCompositorError_IsNotSceneApplication: return "Is not scene application";
43 case vr::VRCompositorError_TextureIsOnWrongDevice: return "Texture is on wrong device";
44 case vr::VRCompositorError_TextureUsesUnsupportedFormat: return "Texture uses unsupported format";
45 case vr::VRCompositorError_SharedTexturesNotSupported: return "Shared textures not supported";
46 case vr::VRCompositorError_IndexOutOfRange: return "Index out of range";
47 case vr::VRCompositorError_AlreadySubmitted: return "Already submitted";
48 case vr::VRCompositorError_InvalidBounds: return "Invalid Bounds";
49 case vr::VRCompositorError_AlreadySet: return "Already set";
54 //! Print OpenVR input error.
55 static const char* getVRInputError (vr::EVRInputError theVRError)
59 case vr::VRInputError_None: return "None";
60 case vr::VRInputError_NameNotFound: return "NameNotFound";
61 case vr::VRInputError_WrongType: return "WrongType";
62 case vr::VRInputError_InvalidHandle: return "InvalidHandle";
63 case vr::VRInputError_InvalidParam: return "InvalidParam";
64 case vr::VRInputError_NoSteam: return "NoSteam:";
65 case vr::VRInputError_MaxCapacityReached: return "MaxCapacityReached";
66 case vr::VRInputError_IPCError: return "IPCError";
67 case vr::VRInputError_NoActiveActionSet: return "NoActiveActionSet";
68 case vr::VRInputError_InvalidDevice: return "InvalidDevice";
69 case vr::VRInputError_InvalidSkeleton: return "InvalidSkeleton";
70 case vr::VRInputError_InvalidBoneCount: return "InvalidBoneCount";
71 case vr::VRInputError_InvalidCompressedData: return "InvalidCompressedData";
72 case vr::VRInputError_NoData: return "NoData";
73 case vr::VRInputError_BufferTooSmall: return "BufferTooSmall";
74 case vr::VRInputError_MismatchedActionManifest: return "MismatchedActionManifest";
75 case vr::VRInputError_MissingSkeletonData: return "MissingSkeletonData";
76 case vr::VRInputError_InvalidBoneIndex: return "InvalidBoneIndex";
77 case vr::VRInputError_InvalidPriority: return "InvalidPriority";
78 case vr::VRInputError_PermissionDenied: return "PermissionDenied";
79 case vr::VRInputError_InvalidRenderModel: return "InvalidRenderModel";
84 //! Convert OpenVR mat4x4 into OCCT mat4x4.
85 static NCollection_Mat4<double> mat44vr2Occ (const vr::HmdMatrix44_t& theMat4)
87 NCollection_Mat4<double> aMat4;
88 for (int aRow = 0; aRow < 4; ++aRow)
90 aMat4.SetRow (aRow, NCollection_Vec4<double> (theMat4.m[aRow][0], theMat4.m[aRow][1], theMat4.m[aRow][2], theMat4.m[aRow][3]));
95 //! Convert OpenVR mat3x4 into OCCT gp_Trsf.
96 static gp_Trsf mat34vr2OccTrsf (const vr::HmdMatrix34_t& theMat4)
99 aTrsf.SetValues (theMat4.m[0][0], theMat4.m[0][1], theMat4.m[0][2], theMat4.m[0][3],
100 theMat4.m[1][0], theMat4.m[1][1], theMat4.m[1][2], theMat4.m[1][3],
101 theMat4.m[2][0], theMat4.m[2][1], theMat4.m[2][2], theMat4.m[2][3]);
105 //! Convert OpenVR tracked pose.
106 static Aspect_TrackedDevicePose poseVr2Occ (const vr::TrackedDevicePose_t& theVrPose,
107 const Standard_Real theUnitFactor)
109 Aspect_TrackedDevicePose aPose;
110 aPose.Velocity.SetCoord (theVrPose.vVelocity.v[0], theVrPose.vVelocity.v[1], theVrPose.vVelocity.v[2]);
111 aPose.AngularVelocity.SetCoord (theVrPose.vAngularVelocity.v[0], theVrPose.vAngularVelocity.v[1], theVrPose.vAngularVelocity.v[2]);
112 aPose.IsValidPose = theVrPose.bPoseIsValid;
113 aPose.IsConnectedDevice = theVrPose.bDeviceIsConnected;
114 if (aPose.IsValidPose)
116 aPose.Orientation = mat34vr2OccTrsf (theVrPose.mDeviceToAbsoluteTracking);
117 if (theUnitFactor != 1.0)
119 aPose.Orientation.SetTranslationPart (aPose.Orientation.TranslationPart() * theUnitFactor);
125 //! Find location of default actions manifest file (based on CSF_OCCTResourcePath or CASROOT variables).
126 TCollection_AsciiString defaultActionsManifestInit()
128 const TCollection_AsciiString THE_ACTIONS_JSON = "occtvr_actions.json";
129 const TCollection_AsciiString aResRoot (OSD_Environment ("CSF_OCCTResourcePath").Value());
130 if (!aResRoot.IsEmpty())
132 if (OSD_File (aResRoot + "/" + THE_ACTIONS_JSON).Exists())
134 return aResRoot + "/" + THE_ACTIONS_JSON;
136 if (OSD_File (aResRoot + "/XRResources/" + THE_ACTIONS_JSON).Exists())
138 return aResRoot + "/XRResources/" + THE_ACTIONS_JSON;
141 const TCollection_AsciiString aCasRoot (OSD_Environment ("CASROOT").Value());
142 if (!aCasRoot.IsEmpty())
144 if (OSD_File (aCasRoot + "/" + THE_ACTIONS_JSON).Exists())
146 return aCasRoot + "/" + THE_ACTIONS_JSON;
148 if (OSD_File (aCasRoot + "/XRResources/" + THE_ACTIONS_JSON).Exists())
150 return aCasRoot + "/XRResources/" + THE_ACTIONS_JSON;
152 if (OSD_File (aCasRoot + "/XRResources/src/" + THE_ACTIONS_JSON).Exists())
154 return aCasRoot + "/XRResources/src/" + THE_ACTIONS_JSON;
157 return OSD_Process::ExecutablePath() + "/occtvr_actions.json";
162 IMPLEMENT_STANDARD_RTTIEXT(Aspect_OpenVRSession, Aspect_XRSession)
164 struct Aspect_OpenVRSession::VRContext
167 vr::TrackedDevicePose_t TrackedPoses[vr::k_unMaxTrackedDeviceCount]; //!< array of tracked devices poses
168 vr::IVRSystem* System; //!< OpenVR session object
170 //! Empty constructor.
171 Aspect_OpenVRSession::VRContext() : System (NULL)
173 memset (TrackedPoses, 0, sizeof(TrackedPoses));
176 //! IVRSystem::PollNextEvent() wrapper.
177 bool PollNextEvent (vr::VREvent_t& theEvent)
179 return System->PollNextEvent (&theEvent, sizeof(vr::VREvent_t));
182 //! IVRSystem::GetControllerState() wrapper.
183 bool GetControllerState (vr::VRControllerState_t& theState,
184 vr::TrackedDeviceIndex_t theDevice)
186 return System->GetControllerState (theDevice, &theState, sizeof(vr::VRControllerState_t&));
189 //! Retrieve string property from OpenVR.
190 TCollection_AsciiString getVrTrackedDeviceString (vr::TrackedDeviceIndex_t theDevice,
191 vr::TrackedDeviceProperty theProperty,
192 vr::TrackedPropertyError* theError = NULL)
194 const uint32_t aBuffLen = System->GetStringTrackedDeviceProperty(theDevice, theProperty, NULL, 0, theError);
197 return TCollection_AsciiString();
200 NCollection_LocalArray<char> aBuffer (aBuffLen + 1);
201 System->GetStringTrackedDeviceProperty (theDevice, theProperty, aBuffer, aBuffLen, theError);
202 aBuffer[aBuffLen] = '\0';
203 const TCollection_AsciiString aResult (aBuffer);
210 //! Image wrapping vr::RenderModel_TextureMap_t.
211 class Aspect_OpenVRSession::VRImagePixmap : public Image_PixMap
214 //! Empty constructor.
215 VRImagePixmap() : myVrTexture (NULL) {}
217 //! Load the texture.
218 bool Load (vr::TextureID_t theTexture, const TCollection_AsciiString& theVrModelName)
220 vr::RenderModel_TextureMap_t* aVrTexture = NULL;
221 vr::EVRRenderModelError aVrError = vr::VRRenderModelError_Loading;
222 for (; aVrError == vr::VRRenderModelError_Loading;)
224 aVrError = vr::VRRenderModels()->LoadTexture_Async (theTexture, &aVrTexture);
225 OSD::MilliSecSleep (1);
227 if (aVrError != vr::VRRenderModelError_None
228 || aVrTexture == NULL)
230 Message::DefaultMessenger()->Send (TCollection_AsciiString ("OpenVR, Unable to load render model texture: ") + theVrModelName + " - " + int(aVrError), Message_Fail);
234 InitWrapper (Image_Format_RGBA, (Standard_Byte* )aVrTexture->rubTextureMapData, aVrTexture->unWidth, aVrTexture->unHeight);
235 myVrTexture = aVrTexture;
239 virtual ~VRImagePixmap()
241 if (myVrTexture != NULL)
243 vr::VRRenderModels()->FreeTexture (myVrTexture);
247 vr::RenderModel_TextureMap_t* myVrTexture;
250 //! Image_Texture extension using vr::VRRenderModels().
251 class Aspect_OpenVRSession::VRTextureSource : public Image_Texture
255 //! Main constructor.
256 VRTextureSource (vr::TextureID_t theTextureId, const TCollection_AsciiString& theVrModelName)
257 : Image_Texture (""), myVrTextureId (theTextureId), myVrModelName (theVrModelName)
259 myTextureId = TCollection_AsciiString ("texturevr://map_#") + (int )theTextureId;
264 virtual Handle(Image_PixMap) ReadImage() const Standard_OVERRIDE
266 Handle(VRImagePixmap) aPixmap = new VRImagePixmap();
267 if (!aPixmap->Load (myVrTextureId, myVrModelName))
269 return Handle(VRImagePixmap)();
274 vr::TextureID_t myVrTextureId;
275 TCollection_AsciiString myVrModelName;
279 // =======================================================================
280 // function : IsHmdPresent
282 // =======================================================================
283 bool Aspect_OpenVRSession::IsHmdPresent()
286 return vr::VR_IsHmdPresent();
292 // =======================================================================
293 // function : defaultActionsManifest
295 // =======================================================================
296 TCollection_AsciiString Aspect_OpenVRSession::defaultActionsManifest()
299 static const TCollection_AsciiString THE_ACTIONS_JSON_FULL = defaultActionsManifestInit();
300 return THE_ACTIONS_JSON_FULL;
302 return TCollection_AsciiString();
306 // =======================================================================
307 // function : Aspect_OpenVRSession
309 // =======================================================================
310 Aspect_OpenVRSession::Aspect_OpenVRSession()
311 : myContext (new VRContext())
314 myActionsManifest = defaultActionsManifest();
315 myTrackedPoses.Resize (0, (Standard_Integer )vr::k_unMaxTrackedDeviceCount - 1, false);
317 Handle(Aspect_XRActionSet) aHeadActionSet = new Aspect_XRActionSet ("/actions/generic_head");
318 myActionSets.Add (aHeadActionSet->Id(), aHeadActionSet);
320 Handle(Aspect_XRAction) aHeadsetOn = new Aspect_XRAction (aHeadActionSet->Id() + "/in/headset_on_head", Aspect_XRActionType_InputDigital);
321 aHeadActionSet->AddAction (aHeadsetOn);
322 NCollection_Array1<Handle(Aspect_XRAction)>& aGenericSet = myRoleActions[Aspect_XRTrackedDeviceRole_Head];
323 aGenericSet[Aspect_XRGenericAction_IsHeadsetOn] = aHeadsetOn;
325 for (int aHand = 0; aHand < 2; ++aHand)
327 NCollection_Array1<Handle(Aspect_XRAction)>& aGenericSet = myRoleActions[aHand == 0 ? Aspect_XRTrackedDeviceRole_LeftHand : Aspect_XRTrackedDeviceRole_RightHand];
328 Handle(Aspect_XRActionSet) anActionSet = new Aspect_XRActionSet (aHand == 0 ? "/actions/generic_left" : "/actions/generic_right");
329 myActionSets.Add (anActionSet->Id(), anActionSet);
331 Handle(Aspect_XRAction) anAppMenuClick = new Aspect_XRAction (anActionSet->Id() + "/in/appmenu_click", Aspect_XRActionType_InputDigital);
332 anActionSet->AddAction (anAppMenuClick);
333 aGenericSet[Aspect_XRGenericAction_InputAppMenu] = anAppMenuClick;
335 Handle(Aspect_XRAction) aSysMenuClick = new Aspect_XRAction (anActionSet->Id() + "/in/sysmenu_click", Aspect_XRActionType_InputDigital);
336 anActionSet->AddAction (aSysMenuClick);
337 aGenericSet[Aspect_XRGenericAction_InputSysMenu] = aSysMenuClick;
339 Handle(Aspect_XRAction) aTriggerPull = new Aspect_XRAction (anActionSet->Id() + "/in/trigger_pull", Aspect_XRActionType_InputAnalog);
340 anActionSet->AddAction (aTriggerPull);
341 aGenericSet[Aspect_XRGenericAction_InputTriggerPull] = aTriggerPull;
343 Handle(Aspect_XRAction) aTriggerClick = new Aspect_XRAction (anActionSet->Id() + "/in/trigger_click", Aspect_XRActionType_InputDigital);
344 anActionSet->AddAction (aTriggerClick);
345 aGenericSet[Aspect_XRGenericAction_InputTriggerClick] = aTriggerClick;
347 Handle(Aspect_XRAction) aGripClick = new Aspect_XRAction (anActionSet->Id() + "/in/grip_click", Aspect_XRActionType_InputDigital);
348 anActionSet->AddAction (aGripClick);
349 aGenericSet[Aspect_XRGenericAction_InputGripClick] = aGripClick;
351 Handle(Aspect_XRAction) aPadPos = new Aspect_XRAction (anActionSet->Id() + "/in/trackpad_position", Aspect_XRActionType_InputAnalog);
352 anActionSet->AddAction (aPadPos);
353 aGenericSet[Aspect_XRGenericAction_InputTrackPadPosition] = aPadPos;
355 Handle(Aspect_XRAction) aPadTouch = new Aspect_XRAction (anActionSet->Id() + "/in/trackpad_touch", Aspect_XRActionType_InputDigital);
356 anActionSet->AddAction (aPadTouch);
357 aGenericSet[Aspect_XRGenericAction_InputTrackPadTouch] = aPadTouch;
359 Handle(Aspect_XRAction) aPadClick = new Aspect_XRAction (anActionSet->Id() + "/in/trackpad_click", Aspect_XRActionType_InputDigital);
360 anActionSet->AddAction (aPadClick);
361 aGenericSet[Aspect_XRGenericAction_InputTrackPadClick] = aPadClick;
363 Handle(Aspect_XRAction) aPoseBase = new Aspect_XRAction (anActionSet->Id() + "/in/pose_base", Aspect_XRActionType_InputPose);
364 anActionSet->AddAction (aPoseBase);
365 aGenericSet[Aspect_XRGenericAction_InputPoseBase] = aPoseBase;
367 Handle(Aspect_XRAction) aPoseFront = new Aspect_XRAction (anActionSet->Id() + "/in/pose_front", Aspect_XRActionType_InputPose);
368 anActionSet->AddAction (aPoseFront);
369 aGenericSet[Aspect_XRGenericAction_InputPoseFront] = aPoseFront;
371 Handle(Aspect_XRAction) aPoseGrip = new Aspect_XRAction (anActionSet->Id() + "/in/pose_handgrip", Aspect_XRActionType_InputPose);
372 anActionSet->AddAction (aPoseGrip);
373 aGenericSet[Aspect_XRGenericAction_InputPoseHandGrip] = aPoseGrip;
375 Handle(Aspect_XRAction) aPoseTip = new Aspect_XRAction (anActionSet->Id() + "/in/pose_tip", Aspect_XRActionType_InputPose);
376 anActionSet->AddAction (aPoseTip);
377 aGenericSet[Aspect_XRGenericAction_InputPoseFingerTip] = aPoseTip;
379 Handle(Aspect_XRAction) aHaptic = new Aspect_XRAction (anActionSet->Id() + "/out/haptic", Aspect_XRActionType_OutputHaptic);
380 anActionSet->AddAction (aHaptic);
381 aGenericSet[Aspect_XRGenericAction_OutputHaptic] = aHaptic;
383 Handle(Aspect_XRAction) aThumbsctickPos = new Aspect_XRAction (anActionSet->Id() + "/in/thumbstick_position", Aspect_XRActionType_InputAnalog);
384 anActionSet->AddAction (aThumbsctickPos);
385 aGenericSet[Aspect_XRGenericAction_InputThumbstickPosition] = aThumbsctickPos;
387 Handle(Aspect_XRAction) aThumbsctickTouch = new Aspect_XRAction (anActionSet->Id() + "/in/thumbstick_touch", Aspect_XRActionType_InputDigital);
388 anActionSet->AddAction (aThumbsctickTouch);
389 aGenericSet[Aspect_XRGenericAction_InputThumbstickTouch] = aThumbsctickTouch;
391 Handle(Aspect_XRAction) aThumbsctickClick = new Aspect_XRAction (anActionSet->Id() + "/in/thumbstick_click", Aspect_XRActionType_InputDigital);
392 anActionSet->AddAction (aThumbsctickClick);
393 aGenericSet[Aspect_XRGenericAction_InputThumbstickClick] = aThumbsctickClick;
398 // =======================================================================
399 // function : ~Aspect_OpenVRSession
401 // =======================================================================
402 Aspect_OpenVRSession::~Aspect_OpenVRSession()
408 // =======================================================================
409 // function : closeVR
411 // =======================================================================
412 void Aspect_OpenVRSession::closeVR()
415 if (myContext->System != NULL)
418 myContext->System = NULL;
423 // =======================================================================
424 // function : getVRSystem
426 // =======================================================================
427 void* Aspect_OpenVRSession::getVRSystem() const
430 return myContext->System;
436 // =======================================================================
439 // =======================================================================
440 void Aspect_OpenVRSession::Close()
445 // =======================================================================
448 // =======================================================================
449 bool Aspect_OpenVRSession::IsOpen() const
452 return myContext->System != NULL;
458 // =======================================================================
461 // =======================================================================
462 bool Aspect_OpenVRSession::Open()
465 if (myContext->System != NULL)
470 vr::EVRInitError aVrError = vr::VRInitError_None;
471 myContext->System = vr::VR_Init (&aVrError, vr::VRApplication_Scene);
472 if (aVrError != vr::VRInitError_None)
474 myContext->System = NULL;
475 Message::DefaultMessenger()->Send (TCollection_AsciiString ("OpenVR, Unable to init VR runtime: ") + vr::VR_GetVRInitErrorAsEnglishDescription (aVrError),
481 /*vr::IVRRenderModels* aRenderModels = (vr::IVRRenderModels* )vr::VR_GetGenericInterface (vr::IVRRenderModels_Version, &aVrError);
482 if (aRenderModels == NULL)
484 Message::DefaultMessenger()->Send (TCollection_AsciiString ("Unable to get render model interface: ") + vr::VR_GetVRInitErrorAsEnglishDescription (aVrError),
488 NCollection_Vec2<uint32_t> aRenderSize;
489 myContext->System->GetRecommendedRenderTargetSize (&aRenderSize.x(), &aRenderSize.y());
490 myRendSize = NCollection_Vec2<int> (aRenderSize);
491 myDispFreq = myContext->System->GetFloatTrackedDeviceProperty (vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_DisplayFrequency_Float);
492 if (myRendSize.x() <= 0
493 || myRendSize.y() <= 0)
498 updateProjectionFrustums();
502 Message::DefaultMessenger()->Send ("Open CASCADE has been built without OpenVR support", Message_Fail);
507 // =======================================================================
508 // function : initInput
510 // =======================================================================
511 bool Aspect_OpenVRSession::initInput()
514 if (myContext->System == NULL)
519 if (!OSD_File (myActionsManifest).Exists())
521 Message::DefaultMessenger()->Send (TCollection_AsciiString ("OpenVR, Unable to open actions manifest '") + myActionsManifest + "'", Message_Fail);
525 vr::EVRInputError aVrError = vr::VRInput()->SetActionManifestPath (myActionsManifest.ToCString());
526 if (aVrError != vr::VRInputError_None)
528 Message::DefaultMessenger()->Send (TCollection_AsciiString ("OpenVR, Unable to load actions manifest '") + myActionsManifest
529 + "': " + getVRInputError (aVrError), Message_Fail);
533 bool hasErrors = false;
534 for (Aspect_XRActionSetMap::Iterator aSetIter (myActionSets); aSetIter.More(); aSetIter.Next())
536 const Handle(Aspect_XRActionSet)& anActionSet = aSetIter.Value();
537 for (Aspect_XRActionMap::Iterator anActionIter (anActionSet->Actions()); anActionIter.More(); anActionIter.Next())
539 const Handle(Aspect_XRAction)& anAction = anActionIter.Value();
540 vr::VRActionHandle_t anActionHandle = 0;
541 aVrError = vr::VRInput()->GetActionHandle (anAction->Id().ToCString(), &anActionHandle);
542 if (aVrError == vr::VRInputError_None)
544 anAction->SetRawHandle (anActionHandle);
549 Message::DefaultMessenger()->Send (TCollection_AsciiString ("OpenVR, Unable to load action '") + anAction->Id() + "' from '" + myActionsManifest
550 + "': " + getVRInputError (aVrError), Message_Fail);
554 vr::VRActionSetHandle_t anActionSetHandle = 0;
555 aVrError = vr::VRInput()->GetActionSetHandle (anActionSet->Id().ToCString(), &anActionSetHandle);
556 if (aVrError == vr::VRInputError_None)
558 anActionSet->SetRawHandle (anActionSetHandle);
563 Message::DefaultMessenger()->Send (TCollection_AsciiString ("OpenVR, Unable to load action set '") + anActionSet->Id() + "' from '" + myActionsManifest
564 + "': " + getVRInputError (aVrError), Message_Fail);
573 // =======================================================================
574 // function : GetString
576 // =======================================================================
577 TCollection_AsciiString Aspect_OpenVRSession::GetString (InfoString theInfo) const
580 if (myContext->System != NULL)
582 vr::ETrackedDeviceProperty aVrProp = vr::Prop_Invalid;
585 case InfoString_Vendor: aVrProp = vr::Prop_ManufacturerName_String; break;
586 case InfoString_Device: aVrProp = vr::Prop_ModelNumber_String; break;
587 case InfoString_Tracker: aVrProp = vr::Prop_TrackingSystemName_String; break;
588 case InfoString_SerialNumber: aVrProp = vr::Prop_SerialNumber_String; break;
590 if (aVrProp != vr::Prop_Invalid)
592 return myContext->getVrTrackedDeviceString (vr::k_unTrackedDeviceIndex_Hmd, aVrProp);
598 return TCollection_AsciiString();
601 // =======================================================================
602 // function : NamedTrackedDevice
604 // =======================================================================
605 Standard_Integer Aspect_OpenVRSession::NamedTrackedDevice (Aspect_XRTrackedDeviceRole theDevice) const
608 if (myContext->System != NULL)
610 vr::TrackedDeviceIndex_t aDevIndex = vr::k_unTrackedDeviceIndexInvalid;
613 case Aspect_XRTrackedDeviceRole_Head: aDevIndex = vr::k_unTrackedDeviceIndex_Hmd; break;
614 case Aspect_XRTrackedDeviceRole_LeftHand: aDevIndex = myContext->System->GetTrackedDeviceIndexForControllerRole (vr::TrackedControllerRole_LeftHand); break;
615 case Aspect_XRTrackedDeviceRole_RightHand: aDevIndex = myContext->System->GetTrackedDeviceIndexForControllerRole (vr::TrackedControllerRole_RightHand); break;
616 case Aspect_XRTrackedDeviceRole_Other: break;
618 if (aDevIndex == vr::k_unTrackedDeviceIndexInvalid)
622 return (Standard_Integer )aDevIndex;
630 // =======================================================================
631 // function : loadRenderModel
633 // =======================================================================
634 Handle(Graphic3d_ArrayOfTriangles) Aspect_OpenVRSession::loadRenderModel (Standard_Integer theDevice,
635 Standard_Boolean theToApplyUnitFactor,
636 Handle(Image_Texture)& theTexture)
640 return Handle(Graphic3d_ArrayOfTriangles)();
643 if (myContext->System == NULL)
645 return Handle(Graphic3d_ArrayOfTriangles)();
648 const TCollection_AsciiString aRenderModelName = myContext->getVrTrackedDeviceString (theDevice, vr::Prop_RenderModelName_String);
649 if (aRenderModelName.IsEmpty())
651 return Handle(Graphic3d_ArrayOfTriangles)();
654 vr::RenderModel_t* aVrModel = NULL;
655 vr::EVRRenderModelError aVrError = vr::VRRenderModelError_Loading;
656 for (; aVrError == vr::VRRenderModelError_Loading;)
658 aVrError = vr::VRRenderModels()->LoadRenderModel_Async (aRenderModelName.ToCString(), &aVrModel);
659 OSD::MilliSecSleep (1);
661 if (aVrError != vr::VRRenderModelError_None)
663 Message::DefaultMessenger()->Send (TCollection_AsciiString ("OpenVR, Unable to load render model: ") + aRenderModelName + " - " + int(aVrError), Message_Fail);
664 return Handle(Graphic3d_ArrayOfTriangles)();
667 if (aVrModel->diffuseTextureId >= 0)
669 theTexture = new VRTextureSource (aVrModel->diffuseTextureId, aRenderModelName);
672 const float aScale = theToApplyUnitFactor ? float(myUnitFactor) : 1.0f;
673 Handle(Graphic3d_ArrayOfTriangles) aTris = new Graphic3d_ArrayOfTriangles ((int )aVrModel->unVertexCount, (int )aVrModel->unTriangleCount * 3,
674 Graphic3d_ArrayFlags_VertexNormal | Graphic3d_ArrayFlags_VertexTexel);
675 for (uint32_t aVertIter = 0; aVertIter < aVrModel->unVertexCount; ++aVertIter)
677 const vr::RenderModel_Vertex_t& aVert = aVrModel->rVertexData[aVertIter];
678 aTris->AddVertex (aVert.vPosition.v[0] * aScale, aVert.vPosition.v[1] * aScale, aVert.vPosition.v[2] * aScale,
679 aVert.vNormal.v[0], aVert.vNormal.v[1], aVert.vNormal.v[2],
680 aVert.rfTextureCoord[0], aVert.rfTextureCoord[1]);
682 for (uint32_t aTriIter = 0; aTriIter < aVrModel->unTriangleCount; ++aTriIter)
684 aTris->AddEdges (aVrModel->rIndexData[aTriIter * 3 + 0] + 1,
685 aVrModel->rIndexData[aTriIter * 3 + 1] + 1,
686 aVrModel->rIndexData[aTriIter * 3 + 2] + 1);
689 vr::VRRenderModels()->FreeRenderModel (aVrModel);
692 (void )theToApplyUnitFactor;
694 return Handle(Graphic3d_ArrayOfTriangles)();
698 // =======================================================================
699 // function : EyeToHeadTransform
701 // =======================================================================
702 NCollection_Mat4<double> Aspect_OpenVRSession::EyeToHeadTransform (Aspect_Eye theEye) const
705 if (myContext->System != NULL)
707 const vr::HmdMatrix34_t aMatVr = myContext->System->GetEyeToHeadTransform (theEye == Aspect_Eye_Right ? vr::Eye_Right : vr::Eye_Left);
708 gp_Trsf aTrsf = mat34vr2OccTrsf (aMatVr);
709 if (myUnitFactor != 1.0)
711 aTrsf.SetTranslationPart (aTrsf.TranslationPart() * myUnitFactor);
713 NCollection_Mat4<double> aMat4;
714 aTrsf.GetMat4 (aMat4);
720 return NCollection_Mat4<double>();
723 // =======================================================================
724 // function : ProjectionMatrix
726 // =======================================================================
727 NCollection_Mat4<double> Aspect_OpenVRSession::ProjectionMatrix (Aspect_Eye theEye,
729 double theZFar) const
732 if (myContext->System != NULL)
734 const vr::HmdMatrix44_t aMat4 = myContext->System->GetProjectionMatrix (theEye == Aspect_Eye_Right ? vr::Eye_Right : vr::Eye_Left,
735 (float )theZNear, (float )theZFar);
736 return mat44vr2Occ (aMat4);
743 return NCollection_Mat4<double>();
746 // =======================================================================
747 // function : updateProjectionFrustums
749 // =======================================================================
750 void Aspect_OpenVRSession::updateProjectionFrustums()
753 Aspect_FrustumLRBT<float> aFrustL, aFrustR;
754 myContext->System->GetProjectionRaw (vr::Eye_Left, &aFrustL.Left, &aFrustL.Right, &aFrustL.Top, &aFrustL.Bottom);
755 myContext->System->GetProjectionRaw (vr::Eye_Right, &aFrustR.Left, &aFrustR.Right, &aFrustR.Top, &aFrustR.Bottom);
756 myFrustumL = Aspect_FrustumLRBT<double> (aFrustL);
757 myFrustumR = Aspect_FrustumLRBT<double> (aFrustR);
758 std::swap (myFrustumL.Top, myFrustumL.Bottom);
759 std::swap (myFrustumR.Top, myFrustumR.Bottom);
761 const NCollection_Vec2<double> aTanHalfFov (NCollection_Vec4<float>(-aFrustL.Left, aFrustL.Right, -aFrustR.Left, aFrustR.Right).maxComp(),
762 NCollection_Vec4<float>(-aFrustL.Top, aFrustL.Bottom, -aFrustR.Top, aFrustR.Bottom).maxComp());
763 myAspect = aTanHalfFov.x() / aTanHalfFov.y();
764 myFieldOfView = 2.0 * ATan(aTanHalfFov.y()) * 180.0 / M_PI;
766 // Intra-ocular Distance can be changed in runtime
767 //const vr::HmdMatrix34_t aLeftToHead = myContext->System->GetEyeToHeadTransform (vr::Eye_Left);
768 //const vr::HmdMatrix34_t aRightToHead = myContext->System->GetEyeToHeadTransform (vr::Eye_Right);
769 //myIod = aRightToHead.m[0][3] - aLeftToHead.m[0][3];
770 myIod = myContext->System->GetFloatTrackedDeviceProperty (vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_UserIpdMeters_Float);
771 myIod *= myUnitFactor;
775 // =======================================================================
776 // function : SetTrackingOrigin
778 // =======================================================================
779 void Aspect_OpenVRSession::SetTrackingOrigin (TrackingUniverseOrigin theOrigin)
782 if (myContext->System != NULL)
784 vr::ETrackingUniverseOrigin anOrigin = vr::TrackingUniverseStanding;
787 case TrackingUniverseOrigin_Seated: anOrigin = vr::TrackingUniverseSeated; break;
788 case TrackingUniverseOrigin_Standing: anOrigin = vr::TrackingUniverseStanding; break;
790 vr::VRCompositor()->SetTrackingSpace (anOrigin);
793 myTrackOrigin = theOrigin;
796 // =======================================================================
797 // function : WaitPoses
799 // =======================================================================
800 bool Aspect_OpenVRSession::WaitPoses()
803 if (myContext->System == NULL)
808 switch (vr::VRCompositor()->GetTrackingSpace())
810 case vr::TrackingUniverseSeated:
811 myTrackOrigin = TrackingUniverseOrigin_Seated;
813 case vr::TrackingUniverseRawAndUncalibrated:
814 case vr::TrackingUniverseStanding:
815 myTrackOrigin = TrackingUniverseOrigin_Standing;
819 const vr::EVRCompositorError aVRError = vr::VRCompositor()->WaitGetPoses (myContext->TrackedPoses, vr::k_unMaxTrackedDeviceCount, NULL, 0);
820 if (aVRError != vr::VRCompositorError_None)
822 Message::DefaultMessenger()->Send (TCollection_AsciiString ("Compositor wait poses error: ") + getVRCompositorError (aVRError), Message_Trace);
825 for (Standard_Integer aPoseIter = myTrackedPoses.Lower(); aPoseIter <= myTrackedPoses.Upper(); ++aPoseIter)
827 const vr::TrackedDevicePose_t& aVrPose = myContext->TrackedPoses[aPoseIter];
828 myTrackedPoses[aPoseIter] = poseVr2Occ (aVrPose, myUnitFactor);
831 if (myContext->TrackedPoses[vr::k_unTrackedDeviceIndex_Hmd].bPoseIsValid)
833 myHeadPose = myTrackedPoses[vr::k_unTrackedDeviceIndex_Hmd].Orientation;
834 updateProjectionFrustums();
836 return aVRError != vr::VRCompositorError_None;
842 // =======================================================================
843 // function : GetDigitalActionData
845 // =======================================================================
846 Aspect_XRDigitalActionData Aspect_OpenVRSession::GetDigitalActionData (const Handle(Aspect_XRAction)& theAction) const
848 if (theAction.IsNull()
849 || theAction->Type() != Aspect_XRActionType_InputDigital)
851 throw Standard_ProgramError("Aspect_OpenVRSession::GetDigitalActionData() called for wrong action");
854 Aspect_XRDigitalActionData anActionData;
856 if (myContext->System != NULL
857 && theAction->RawHandle() != 0)
859 vr::InputDigitalActionData_t aNewData = {};
860 vr::EVRInputError anInErr = vr::VRInput()->GetDigitalActionData (theAction->RawHandle(), &aNewData, sizeof(aNewData), vr::k_ulInvalidInputValueHandle);
861 if (anInErr != vr::VRInputError_None)
863 Message::DefaultMessenger()->Send (TCollection_AsciiString ("Input Error on '") + theAction->Id() + "': " + getVRInputError (anInErr), Message_Fail);
867 anActionData.IsActive = aNewData.bActive;
868 anActionData.IsChanged = aNewData.bChanged;
869 anActionData.IsPressed = aNewData.bState;
870 anActionData.UpdateTime = aNewData.fUpdateTime;
871 anActionData.ActiveOrigin = aNewData.activeOrigin;
877 // =======================================================================
878 // function : GetAnalogActionData
880 // =======================================================================
881 Aspect_XRAnalogActionData Aspect_OpenVRSession::GetAnalogActionData (const Handle(Aspect_XRAction)& theAction) const
883 if (theAction.IsNull()
884 || theAction->Type() != Aspect_XRActionType_InputAnalog)
886 throw Standard_ProgramError("Aspect_OpenVRSession::GetAnalogActionData() called for wrong action");
889 Aspect_XRAnalogActionData anActionData;
891 if (myContext->System != NULL
892 && theAction->RawHandle() != 0)
894 vr::InputAnalogActionData_t aNewData = {};
895 vr::EVRInputError anInErr = vr::VRInput()->GetAnalogActionData (theAction->RawHandle(), &aNewData, sizeof(aNewData), vr::k_ulInvalidInputValueHandle);
896 if (anInErr != vr::VRInputError_None)
898 Message::DefaultMessenger()->Send (TCollection_AsciiString ("Input Error on '") + theAction->Id() + "': " + getVRInputError (anInErr), Message_Fail);
902 anActionData.IsActive = aNewData.bActive;
903 anActionData.VecXYZ.SetValues (aNewData.x, aNewData.y, aNewData.z);
904 anActionData.DeltaXYZ.SetValues (aNewData.deltaX, aNewData.deltaY, aNewData.deltaZ);
905 anActionData.UpdateTime = aNewData.fUpdateTime;
906 anActionData.ActiveOrigin = aNewData.activeOrigin;
912 // =======================================================================
913 // function : GetPoseActionDataForNextFrame
915 // =======================================================================
916 Aspect_XRPoseActionData Aspect_OpenVRSession::GetPoseActionDataForNextFrame (const Handle(Aspect_XRAction)& theAction) const
918 if (theAction.IsNull()
919 || theAction->Type() != Aspect_XRActionType_InputPose)
921 throw Standard_ProgramError("Aspect_OpenVRSession::GetPoseActionDataForNextFrame() called for wrong action");
924 Aspect_XRPoseActionData anActionData;
926 if (myContext->System != NULL
927 && theAction->RawHandle() != 0)
929 vr::ETrackingUniverseOrigin anOrigin = vr::TrackingUniverseSeated;
930 switch (myTrackOrigin)
932 case TrackingUniverseOrigin_Seated: anOrigin = vr::TrackingUniverseSeated; break;
933 case TrackingUniverseOrigin_Standing: anOrigin = vr::TrackingUniverseStanding; break;
935 vr::InputPoseActionData_t aNewData = {};
936 vr::EVRInputError anInErr = vr::VRInput()->GetPoseActionDataForNextFrame (theAction->RawHandle(), anOrigin, &aNewData, sizeof(aNewData), vr::k_ulInvalidInputValueHandle);
937 if (anInErr != vr::VRInputError_None)
939 Message::DefaultMessenger()->Send (TCollection_AsciiString ("Input Error on '") + theAction->Id() + "': " + getVRInputError (anInErr), Message_Fail);
943 anActionData.Pose = poseVr2Occ (aNewData.pose, myUnitFactor);
944 anActionData.IsActive = aNewData.bActive;
945 anActionData.ActiveOrigin = aNewData.activeOrigin;
951 // =======================================================================
952 // function : triggerHapticVibrationAction
954 // =======================================================================
955 void Aspect_OpenVRSession::triggerHapticVibrationAction (const Handle(Aspect_XRAction)& theAction,
956 const Aspect_XRHapticActionData& theParams)
958 if (theAction.IsNull()
959 || theAction->Type() != Aspect_XRActionType_OutputHaptic)
961 throw Standard_ProgramError("Aspect_OpenVRSession::triggerHapticVibrationAction() called for wrong action");
965 if (myContext->System != NULL
966 && theAction->RawHandle() != 0)
968 Aspect_XRHapticActionData aParams = theParams;
969 if (!theParams.IsValid())
971 // preset for aborting
972 aParams.Duration = 0.0f;
973 aParams.Frequency = 1.0f;
974 aParams.Amplitude = 0.1f;
976 vr::EVRInputError anInErr = vr::VRInput()->TriggerHapticVibrationAction (theAction->RawHandle(),
977 aParams.Delay, aParams.Duration, aParams.Frequency, aParams.Amplitude,
978 vr::k_ulInvalidInputValueHandle);
979 if (anInErr != vr::VRInputError_None)
981 Message::DefaultMessenger()->Send (TCollection_AsciiString ("Output Error on '") + theAction->Id() + "': " + getVRInputError (anInErr), Message_Fail);
989 // =======================================================================
990 // function : ProcessEvents
992 // =======================================================================
993 void Aspect_OpenVRSession::ProcessEvents()
996 if (myContext->System == NULL)
1001 // process OpenVR events
1002 vr::VREvent_t aVREvent = {};
1003 for (; myContext->PollNextEvent (aVREvent);)
1005 switch (aVREvent.eventType)
1007 case vr::VREvent_TrackedDeviceActivated: onTrackedDeviceActivated ((int )aVREvent.trackedDeviceIndex); break;
1008 case vr::VREvent_TrackedDeviceDeactivated: onTrackedDeviceDeactivated((int )aVREvent.trackedDeviceIndex); break;
1009 case vr::VREvent_TrackedDeviceUpdated: onTrackedDeviceUpdated ((int )aVREvent.trackedDeviceIndex); break;
1013 // process OpenVR action state
1014 if (myActionSets.Extent() > 0)
1016 NCollection_LocalArray<vr::VRActiveActionSet_t, 8> anActionSets (myActionSets.Extent());
1017 memset (anActionSets, 0, sizeof(vr::VRActiveActionSet_t) * myActionSets.Extent());
1018 for (Standard_Integer aSetIter = 0; aSetIter < myActionSets.Extent(); ++aSetIter)
1020 anActionSets[aSetIter].ulActionSet = myActionSets.FindFromIndex (aSetIter + 1)->RawHandle();
1022 vr::VRInput()->UpdateActionState (anActionSets, sizeof(vr::VRActiveActionSet_t), myActionSets.Extent());
1027 for (Aspect_XRActionSetMap::Iterator aSetIter (myActionSets); aSetIter.More(); aSetIter.Next())
1029 const Handle(Aspect_XRActionSet)& anActionSet = aSetIter.Value();
1030 for (Aspect_XRActionMap::Iterator anActionIter (anActionSet->Actions()); anActionIter.More(); anActionIter.Next())
1032 const Handle(Aspect_XRAction)& anAction = anActionIter.Value();
1033 if (anAction->RawHandle() == 0
1034 || anAction->Id().IsEmpty())
1039 switch (anAction->Type())
1041 case Aspect_XRActionType_InputDigital:
1043 Aspect_XRDigitalActionData aData = GetDigitalActionData (anAction);
1044 //if (aData.IsChanged) { std::cout << " " << anAction->Id() << " pressed: " << aData.IsPressed << "\n"; }
1047 case Aspect_XRActionType_InputAnalog:
1049 Aspect_XRAnalogActionData aData = GetAnalogActionData (anAction);
1050 //if (aData.IsChanged()) { std::cout << " " << anAction->Id() << " changed: " << aData.VecXYZ[0] << " " << aData.VecXYZ[1] << " " << aData.VecXYZ[2] << "\n"; }
1053 case Aspect_XRActionType_InputPose:
1055 GetPoseActionDataForNextFrame (anAction);
1062 // process OpenVR controller state using deprecated API
1063 //for (vr::TrackedDeviceIndex_t aDevIter = 0; aDevIter < vr::k_unMaxTrackedDeviceCount; ++aDevIter) {
1064 // vr::VRControllerState_t aCtrlState = {}; if (myContext->GetControllerState (aCtrlState, aDevIter)) { aCtrlState.ulButtonPressed == 0; }
1069 // =======================================================================
1070 // function : onTrackedDeviceActivated
1072 // =======================================================================
1073 void Aspect_OpenVRSession::onTrackedDeviceActivated (Standard_Integer theDeviceIndex)
1075 Message::DefaultMessenger()->Send (TCollection_AsciiString ("OpenVR, Device ") + theDeviceIndex + " attached", Message_Trace);
1078 // =======================================================================
1079 // function : onTrackedDeviceDeactivated
1081 // =======================================================================
1082 void Aspect_OpenVRSession::onTrackedDeviceDeactivated (Standard_Integer theDeviceIndex)
1084 Message::DefaultMessenger()->Send (TCollection_AsciiString ("OpenVR, Device ") + theDeviceIndex + " detached", Message_Trace);
1087 // =======================================================================
1088 // function : onTrackedDeviceUpdated
1090 // =======================================================================
1091 void Aspect_OpenVRSession::onTrackedDeviceUpdated (Standard_Integer theDeviceIndex)
1093 Message::DefaultMessenger()->Send (TCollection_AsciiString ("OpenVR, Device ") + theDeviceIndex + " updated", Message_Trace);
1096 // =======================================================================
1097 // function : SubmitEye
1099 // =======================================================================
1100 bool Aspect_OpenVRSession::SubmitEye (void* theTexture,
1101 Aspect_GraphicsLibrary theGraphicsLib,
1102 Aspect_ColorSpace theColorSpace,
1105 if (theTexture == NULL)
1110 if (myContext->System == NULL)
1115 vr::Texture_t aVRTexture = { (void* )theTexture, vr::TextureType_OpenGL, vr::ColorSpace_Gamma };
1116 switch (theGraphicsLib)
1118 case Aspect_GraphicsLibrary_OpenGL:
1119 aVRTexture.eType = vr::TextureType_OpenGL;
1122 Message::DefaultMessenger()->Send ("Compositor error: unsupported Graphics API", Message_Fail);
1125 switch (theColorSpace)
1127 case Aspect_ColorSpace_sRGB: aVRTexture.eColorSpace = vr::ColorSpace_Gamma; break;
1128 case Aspect_ColorSpace_Linear: aVRTexture.eColorSpace = vr::ColorSpace_Linear; break;
1131 const vr::EVRCompositorError aVRError = vr::VRCompositor()->Submit (theEye == Aspect_Eye_Right ? vr::Eye_Right : vr::Eye_Left, &aVRTexture);
1132 if (aVRError != vr::VRCompositorError_None)
1134 if (aVRError != vr::VRCompositorError_AlreadySubmitted)
1136 Message::DefaultMessenger()->Send (TCollection_AsciiString ("Compositor Error: ") + getVRCompositorError (aVRError), Message_Fail);
1140 Message::DefaultMessenger()->Send (TCollection_AsciiString ("Compositor Error: ") + getVRCompositorError (aVRError), Message_Trace);
1146 (void )theGraphicsLib;
1147 (void )theColorSpace;