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::SendFail (TCollection_AsciiString ("OpenVR, Unable to load render model texture: ") + theVrModelName + " - " + int(aVrError));
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::SendFail (TCollection_AsciiString ("OpenVR, Unable to init VR runtime: ") + vr::VR_GetVRInitErrorAsEnglishDescription (aVrError));
480 /*vr::IVRRenderModels* aRenderModels = (vr::IVRRenderModels* )vr::VR_GetGenericInterface (vr::IVRRenderModels_Version, &aVrError);
481 if (aRenderModels == NULL)
483 Message::SendFail (TCollection_AsciiString ("Unable to get render model interface: ") + vr::VR_GetVRInitErrorAsEnglishDescription (aVrError));;
486 NCollection_Vec2<uint32_t> aRenderSize;
487 myContext->System->GetRecommendedRenderTargetSize (&aRenderSize.x(), &aRenderSize.y());
488 myRendSize = NCollection_Vec2<int> (aRenderSize);
489 myDispFreq = myContext->System->GetFloatTrackedDeviceProperty (vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_DisplayFrequency_Float);
490 if (myRendSize.x() <= 0
491 || myRendSize.y() <= 0)
496 updateProjectionFrustums();
500 Message::SendFail ("Open CASCADE has been built without OpenVR support");
505 // =======================================================================
506 // function : initInput
508 // =======================================================================
509 bool Aspect_OpenVRSession::initInput()
512 if (myContext->System == NULL)
517 if (!OSD_File (myActionsManifest).Exists())
519 Message::SendFail (TCollection_AsciiString ("OpenVR, Unable to open actions manifest '") + myActionsManifest + "'");
523 vr::EVRInputError aVrError = vr::VRInput()->SetActionManifestPath (myActionsManifest.ToCString());
524 if (aVrError != vr::VRInputError_None)
526 Message::SendFail (TCollection_AsciiString ("OpenVR, Unable to load actions manifest '") + myActionsManifest
527 + "': " + getVRInputError (aVrError));
531 bool hasErrors = false;
532 for (Aspect_XRActionSetMap::Iterator aSetIter (myActionSets); aSetIter.More(); aSetIter.Next())
534 const Handle(Aspect_XRActionSet)& anActionSet = aSetIter.Value();
535 for (Aspect_XRActionMap::Iterator anActionIter (anActionSet->Actions()); anActionIter.More(); anActionIter.Next())
537 const Handle(Aspect_XRAction)& anAction = anActionIter.Value();
538 vr::VRActionHandle_t anActionHandle = 0;
539 aVrError = vr::VRInput()->GetActionHandle (anAction->Id().ToCString(), &anActionHandle);
540 if (aVrError == vr::VRInputError_None)
542 anAction->SetRawHandle (anActionHandle);
547 Message::SendFail (TCollection_AsciiString ("OpenVR, Unable to load action '") + anAction->Id() + "' from '" + myActionsManifest
548 + "': " + getVRInputError (aVrError));
552 vr::VRActionSetHandle_t anActionSetHandle = 0;
553 aVrError = vr::VRInput()->GetActionSetHandle (anActionSet->Id().ToCString(), &anActionSetHandle);
554 if (aVrError == vr::VRInputError_None)
556 anActionSet->SetRawHandle (anActionSetHandle);
561 Message::SendFail (TCollection_AsciiString ("OpenVR, Unable to load action set '") + anActionSet->Id() + "' from '" + myActionsManifest
562 + "': " + getVRInputError (aVrError));
571 // =======================================================================
572 // function : GetString
574 // =======================================================================
575 TCollection_AsciiString Aspect_OpenVRSession::GetString (InfoString theInfo) const
578 if (myContext->System != NULL)
580 vr::ETrackedDeviceProperty aVrProp = vr::Prop_Invalid;
583 case InfoString_Vendor: aVrProp = vr::Prop_ManufacturerName_String; break;
584 case InfoString_Device: aVrProp = vr::Prop_ModelNumber_String; break;
585 case InfoString_Tracker: aVrProp = vr::Prop_TrackingSystemName_String; break;
586 case InfoString_SerialNumber: aVrProp = vr::Prop_SerialNumber_String; break;
588 if (aVrProp != vr::Prop_Invalid)
590 return myContext->getVrTrackedDeviceString (vr::k_unTrackedDeviceIndex_Hmd, aVrProp);
596 return TCollection_AsciiString();
599 // =======================================================================
600 // function : NamedTrackedDevice
602 // =======================================================================
603 Standard_Integer Aspect_OpenVRSession::NamedTrackedDevice (Aspect_XRTrackedDeviceRole theDevice) const
606 if (myContext->System != NULL)
608 vr::TrackedDeviceIndex_t aDevIndex = vr::k_unTrackedDeviceIndexInvalid;
611 case Aspect_XRTrackedDeviceRole_Head: aDevIndex = vr::k_unTrackedDeviceIndex_Hmd; break;
612 case Aspect_XRTrackedDeviceRole_LeftHand: aDevIndex = myContext->System->GetTrackedDeviceIndexForControllerRole (vr::TrackedControllerRole_LeftHand); break;
613 case Aspect_XRTrackedDeviceRole_RightHand: aDevIndex = myContext->System->GetTrackedDeviceIndexForControllerRole (vr::TrackedControllerRole_RightHand); break;
614 case Aspect_XRTrackedDeviceRole_Other: break;
616 if (aDevIndex == vr::k_unTrackedDeviceIndexInvalid)
620 return (Standard_Integer )aDevIndex;
628 // =======================================================================
629 // function : loadRenderModel
631 // =======================================================================
632 Handle(Graphic3d_ArrayOfTriangles) Aspect_OpenVRSession::loadRenderModel (Standard_Integer theDevice,
633 Standard_Boolean theToApplyUnitFactor,
634 Handle(Image_Texture)& theTexture)
638 return Handle(Graphic3d_ArrayOfTriangles)();
641 if (myContext->System == NULL)
643 return Handle(Graphic3d_ArrayOfTriangles)();
646 const TCollection_AsciiString aRenderModelName = myContext->getVrTrackedDeviceString (theDevice, vr::Prop_RenderModelName_String);
647 if (aRenderModelName.IsEmpty())
649 return Handle(Graphic3d_ArrayOfTriangles)();
652 vr::RenderModel_t* aVrModel = NULL;
653 vr::EVRRenderModelError aVrError = vr::VRRenderModelError_Loading;
654 for (; aVrError == vr::VRRenderModelError_Loading;)
656 aVrError = vr::VRRenderModels()->LoadRenderModel_Async (aRenderModelName.ToCString(), &aVrModel);
657 OSD::MilliSecSleep (1);
659 if (aVrError != vr::VRRenderModelError_None)
661 Message::SendFail (TCollection_AsciiString ("OpenVR, Unable to load render model: ") + aRenderModelName + " - " + int(aVrError));
662 return Handle(Graphic3d_ArrayOfTriangles)();
665 if (aVrModel->diffuseTextureId >= 0)
667 theTexture = new VRTextureSource (aVrModel->diffuseTextureId, aRenderModelName);
670 const float aScale = theToApplyUnitFactor ? float(myUnitFactor) : 1.0f;
671 Handle(Graphic3d_ArrayOfTriangles) aTris = new Graphic3d_ArrayOfTriangles ((int )aVrModel->unVertexCount, (int )aVrModel->unTriangleCount * 3,
672 Graphic3d_ArrayFlags_VertexNormal | Graphic3d_ArrayFlags_VertexTexel);
673 for (uint32_t aVertIter = 0; aVertIter < aVrModel->unVertexCount; ++aVertIter)
675 const vr::RenderModel_Vertex_t& aVert = aVrModel->rVertexData[aVertIter];
676 aTris->AddVertex (aVert.vPosition.v[0] * aScale, aVert.vPosition.v[1] * aScale, aVert.vPosition.v[2] * aScale,
677 aVert.vNormal.v[0], aVert.vNormal.v[1], aVert.vNormal.v[2],
678 aVert.rfTextureCoord[0], aVert.rfTextureCoord[1]);
680 for (uint32_t aTriIter = 0; aTriIter < aVrModel->unTriangleCount; ++aTriIter)
682 aTris->AddEdges (aVrModel->rIndexData[aTriIter * 3 + 0] + 1,
683 aVrModel->rIndexData[aTriIter * 3 + 1] + 1,
684 aVrModel->rIndexData[aTriIter * 3 + 2] + 1);
687 vr::VRRenderModels()->FreeRenderModel (aVrModel);
690 (void )theToApplyUnitFactor;
692 return Handle(Graphic3d_ArrayOfTriangles)();
696 // =======================================================================
697 // function : EyeToHeadTransform
699 // =======================================================================
700 NCollection_Mat4<double> Aspect_OpenVRSession::EyeToHeadTransform (Aspect_Eye theEye) const
703 if (myContext->System != NULL)
705 const vr::HmdMatrix34_t aMatVr = myContext->System->GetEyeToHeadTransform (theEye == Aspect_Eye_Right ? vr::Eye_Right : vr::Eye_Left);
706 gp_Trsf aTrsf = mat34vr2OccTrsf (aMatVr);
707 if (myUnitFactor != 1.0)
709 aTrsf.SetTranslationPart (aTrsf.TranslationPart() * myUnitFactor);
711 NCollection_Mat4<double> aMat4;
712 aTrsf.GetMat4 (aMat4);
718 return NCollection_Mat4<double>();
721 // =======================================================================
722 // function : ProjectionMatrix
724 // =======================================================================
725 NCollection_Mat4<double> Aspect_OpenVRSession::ProjectionMatrix (Aspect_Eye theEye,
727 double theZFar) const
730 if (myContext->System != NULL)
732 const vr::HmdMatrix44_t aMat4 = myContext->System->GetProjectionMatrix (theEye == Aspect_Eye_Right ? vr::Eye_Right : vr::Eye_Left,
733 (float )theZNear, (float )theZFar);
734 return mat44vr2Occ (aMat4);
741 return NCollection_Mat4<double>();
744 // =======================================================================
745 // function : updateProjectionFrustums
747 // =======================================================================
748 void Aspect_OpenVRSession::updateProjectionFrustums()
751 Aspect_FrustumLRBT<float> aFrustL, aFrustR;
752 myContext->System->GetProjectionRaw (vr::Eye_Left, &aFrustL.Left, &aFrustL.Right, &aFrustL.Top, &aFrustL.Bottom);
753 myContext->System->GetProjectionRaw (vr::Eye_Right, &aFrustR.Left, &aFrustR.Right, &aFrustR.Top, &aFrustR.Bottom);
754 myFrustumL = Aspect_FrustumLRBT<double> (aFrustL);
755 myFrustumR = Aspect_FrustumLRBT<double> (aFrustR);
756 std::swap (myFrustumL.Top, myFrustumL.Bottom);
757 std::swap (myFrustumR.Top, myFrustumR.Bottom);
759 const NCollection_Vec2<double> aTanHalfFov (NCollection_Vec4<float>(-aFrustL.Left, aFrustL.Right, -aFrustR.Left, aFrustR.Right).maxComp(),
760 NCollection_Vec4<float>(-aFrustL.Top, aFrustL.Bottom, -aFrustR.Top, aFrustR.Bottom).maxComp());
761 myAspect = aTanHalfFov.x() / aTanHalfFov.y();
762 myFieldOfView = 2.0 * ATan(aTanHalfFov.y()) * 180.0 / M_PI;
764 // Intra-ocular Distance can be changed in runtime
765 //const vr::HmdMatrix34_t aLeftToHead = myContext->System->GetEyeToHeadTransform (vr::Eye_Left);
766 //const vr::HmdMatrix34_t aRightToHead = myContext->System->GetEyeToHeadTransform (vr::Eye_Right);
767 //myIod = aRightToHead.m[0][3] - aLeftToHead.m[0][3];
768 myIod = myContext->System->GetFloatTrackedDeviceProperty (vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_UserIpdMeters_Float);
769 myIod *= myUnitFactor;
773 // =======================================================================
774 // function : SetTrackingOrigin
776 // =======================================================================
777 void Aspect_OpenVRSession::SetTrackingOrigin (TrackingUniverseOrigin theOrigin)
780 if (myContext->System != NULL)
782 vr::ETrackingUniverseOrigin anOrigin = vr::TrackingUniverseStanding;
785 case TrackingUniverseOrigin_Seated: anOrigin = vr::TrackingUniverseSeated; break;
786 case TrackingUniverseOrigin_Standing: anOrigin = vr::TrackingUniverseStanding; break;
788 vr::VRCompositor()->SetTrackingSpace (anOrigin);
791 myTrackOrigin = theOrigin;
794 // =======================================================================
795 // function : WaitPoses
797 // =======================================================================
798 bool Aspect_OpenVRSession::WaitPoses()
801 if (myContext->System == NULL)
806 switch (vr::VRCompositor()->GetTrackingSpace())
808 case vr::TrackingUniverseSeated:
809 myTrackOrigin = TrackingUniverseOrigin_Seated;
811 case vr::TrackingUniverseRawAndUncalibrated:
812 case vr::TrackingUniverseStanding:
813 myTrackOrigin = TrackingUniverseOrigin_Standing;
817 const vr::EVRCompositorError aVRError = vr::VRCompositor()->WaitGetPoses (myContext->TrackedPoses, vr::k_unMaxTrackedDeviceCount, NULL, 0);
818 if (aVRError != vr::VRCompositorError_None)
820 Message::SendTrace (TCollection_AsciiString ("Compositor wait poses error: ") + getVRCompositorError (aVRError));
823 for (Standard_Integer aPoseIter = myTrackedPoses.Lower(); aPoseIter <= myTrackedPoses.Upper(); ++aPoseIter)
825 const vr::TrackedDevicePose_t& aVrPose = myContext->TrackedPoses[aPoseIter];
826 myTrackedPoses[aPoseIter] = poseVr2Occ (aVrPose, myUnitFactor);
829 if (myContext->TrackedPoses[vr::k_unTrackedDeviceIndex_Hmd].bPoseIsValid)
831 myHeadPose = myTrackedPoses[vr::k_unTrackedDeviceIndex_Hmd].Orientation;
832 updateProjectionFrustums();
834 return aVRError != vr::VRCompositorError_None;
840 // =======================================================================
841 // function : GetDigitalActionData
843 // =======================================================================
844 Aspect_XRDigitalActionData Aspect_OpenVRSession::GetDigitalActionData (const Handle(Aspect_XRAction)& theAction) const
846 if (theAction.IsNull()
847 || theAction->Type() != Aspect_XRActionType_InputDigital)
849 throw Standard_ProgramError("Aspect_OpenVRSession::GetDigitalActionData() called for wrong action");
852 Aspect_XRDigitalActionData anActionData;
854 if (myContext->System != NULL
855 && theAction->RawHandle() != 0)
857 vr::InputDigitalActionData_t aNewData = {};
858 vr::EVRInputError anInErr = vr::VRInput()->GetDigitalActionData (theAction->RawHandle(), &aNewData, sizeof(aNewData), vr::k_ulInvalidInputValueHandle);
859 if (anInErr != vr::VRInputError_None)
861 Message::SendFail (TCollection_AsciiString ("Input Error on '") + theAction->Id() + "': " + getVRInputError (anInErr));
865 anActionData.IsActive = aNewData.bActive;
866 anActionData.IsChanged = aNewData.bChanged;
867 anActionData.IsPressed = aNewData.bState;
868 anActionData.UpdateTime = aNewData.fUpdateTime;
869 anActionData.ActiveOrigin = aNewData.activeOrigin;
875 // =======================================================================
876 // function : GetAnalogActionData
878 // =======================================================================
879 Aspect_XRAnalogActionData Aspect_OpenVRSession::GetAnalogActionData (const Handle(Aspect_XRAction)& theAction) const
881 if (theAction.IsNull()
882 || theAction->Type() != Aspect_XRActionType_InputAnalog)
884 throw Standard_ProgramError("Aspect_OpenVRSession::GetAnalogActionData() called for wrong action");
887 Aspect_XRAnalogActionData anActionData;
889 if (myContext->System != NULL
890 && theAction->RawHandle() != 0)
892 vr::InputAnalogActionData_t aNewData = {};
893 vr::EVRInputError anInErr = vr::VRInput()->GetAnalogActionData (theAction->RawHandle(), &aNewData, sizeof(aNewData), vr::k_ulInvalidInputValueHandle);
894 if (anInErr != vr::VRInputError_None)
896 Message::SendFail (TCollection_AsciiString ("Input Error on '") + theAction->Id() + "': " + getVRInputError (anInErr));
900 anActionData.IsActive = aNewData.bActive;
901 anActionData.VecXYZ.SetValues (aNewData.x, aNewData.y, aNewData.z);
902 anActionData.DeltaXYZ.SetValues (aNewData.deltaX, aNewData.deltaY, aNewData.deltaZ);
903 anActionData.UpdateTime = aNewData.fUpdateTime;
904 anActionData.ActiveOrigin = aNewData.activeOrigin;
910 // =======================================================================
911 // function : GetPoseActionDataForNextFrame
913 // =======================================================================
914 Aspect_XRPoseActionData Aspect_OpenVRSession::GetPoseActionDataForNextFrame (const Handle(Aspect_XRAction)& theAction) const
916 if (theAction.IsNull()
917 || theAction->Type() != Aspect_XRActionType_InputPose)
919 throw Standard_ProgramError("Aspect_OpenVRSession::GetPoseActionDataForNextFrame() called for wrong action");
922 Aspect_XRPoseActionData anActionData;
924 if (myContext->System != NULL
925 && theAction->RawHandle() != 0)
927 vr::ETrackingUniverseOrigin anOrigin = vr::TrackingUniverseSeated;
928 switch (myTrackOrigin)
930 case TrackingUniverseOrigin_Seated: anOrigin = vr::TrackingUniverseSeated; break;
931 case TrackingUniverseOrigin_Standing: anOrigin = vr::TrackingUniverseStanding; break;
933 vr::InputPoseActionData_t aNewData = {};
934 vr::EVRInputError anInErr = vr::VRInput()->GetPoseActionDataForNextFrame (theAction->RawHandle(), anOrigin, &aNewData, sizeof(aNewData), vr::k_ulInvalidInputValueHandle);
935 if (anInErr != vr::VRInputError_None)
937 Message::SendFail (TCollection_AsciiString ("Input Error on '") + theAction->Id() + "': " + getVRInputError (anInErr));
941 anActionData.Pose = poseVr2Occ (aNewData.pose, myUnitFactor);
942 anActionData.IsActive = aNewData.bActive;
943 anActionData.ActiveOrigin = aNewData.activeOrigin;
949 // =======================================================================
950 // function : triggerHapticVibrationAction
952 // =======================================================================
953 void Aspect_OpenVRSession::triggerHapticVibrationAction (const Handle(Aspect_XRAction)& theAction,
954 const Aspect_XRHapticActionData& theParams)
956 if (theAction.IsNull()
957 || theAction->Type() != Aspect_XRActionType_OutputHaptic)
959 throw Standard_ProgramError("Aspect_OpenVRSession::triggerHapticVibrationAction() called for wrong action");
963 if (myContext->System != NULL
964 && theAction->RawHandle() != 0)
966 Aspect_XRHapticActionData aParams = theParams;
967 if (!theParams.IsValid())
969 // preset for aborting
970 aParams.Duration = 0.0f;
971 aParams.Frequency = 1.0f;
972 aParams.Amplitude = 0.1f;
974 vr::EVRInputError anInErr = vr::VRInput()->TriggerHapticVibrationAction (theAction->RawHandle(),
975 aParams.Delay, aParams.Duration, aParams.Frequency, aParams.Amplitude,
976 vr::k_ulInvalidInputValueHandle);
977 if (anInErr != vr::VRInputError_None)
979 Message::SendFail (TCollection_AsciiString ("Output Error on '") + theAction->Id() + "': " + getVRInputError (anInErr));
987 // =======================================================================
988 // function : ProcessEvents
990 // =======================================================================
991 void Aspect_OpenVRSession::ProcessEvents()
994 if (myContext->System == NULL)
999 // process OpenVR events
1000 vr::VREvent_t aVREvent = {};
1001 for (; myContext->PollNextEvent (aVREvent);)
1003 switch (aVREvent.eventType)
1005 case vr::VREvent_TrackedDeviceActivated: onTrackedDeviceActivated ((int )aVREvent.trackedDeviceIndex); break;
1006 case vr::VREvent_TrackedDeviceDeactivated: onTrackedDeviceDeactivated((int )aVREvent.trackedDeviceIndex); break;
1007 case vr::VREvent_TrackedDeviceUpdated: onTrackedDeviceUpdated ((int )aVREvent.trackedDeviceIndex); break;
1011 // process OpenVR action state
1012 if (myActionSets.Extent() > 0)
1014 NCollection_LocalArray<vr::VRActiveActionSet_t, 8> anActionSets (myActionSets.Extent());
1015 memset (anActionSets, 0, sizeof(vr::VRActiveActionSet_t) * myActionSets.Extent());
1016 for (Standard_Integer aSetIter = 0; aSetIter < myActionSets.Extent(); ++aSetIter)
1018 anActionSets[aSetIter].ulActionSet = myActionSets.FindFromIndex (aSetIter + 1)->RawHandle();
1020 vr::VRInput()->UpdateActionState (anActionSets, sizeof(vr::VRActiveActionSet_t), myActionSets.Extent());
1025 for (Aspect_XRActionSetMap::Iterator aSetIter (myActionSets); aSetIter.More(); aSetIter.Next())
1027 const Handle(Aspect_XRActionSet)& anActionSet = aSetIter.Value();
1028 for (Aspect_XRActionMap::Iterator anActionIter (anActionSet->Actions()); anActionIter.More(); anActionIter.Next())
1030 const Handle(Aspect_XRAction)& anAction = anActionIter.Value();
1031 if (anAction->RawHandle() == 0
1032 || anAction->Id().IsEmpty())
1037 switch (anAction->Type())
1039 case Aspect_XRActionType_InputDigital:
1041 Aspect_XRDigitalActionData aData = GetDigitalActionData (anAction);
1042 //if (aData.IsChanged) { std::cout << " " << anAction->Id() << " pressed: " << aData.IsPressed << "\n"; }
1045 case Aspect_XRActionType_InputAnalog:
1047 Aspect_XRAnalogActionData aData = GetAnalogActionData (anAction);
1048 //if (aData.IsChanged()) { std::cout << " " << anAction->Id() << " changed: " << aData.VecXYZ[0] << " " << aData.VecXYZ[1] << " " << aData.VecXYZ[2] << "\n"; }
1051 case Aspect_XRActionType_InputPose:
1053 GetPoseActionDataForNextFrame (anAction);
1060 // process OpenVR controller state using deprecated API
1061 //for (vr::TrackedDeviceIndex_t aDevIter = 0; aDevIter < vr::k_unMaxTrackedDeviceCount; ++aDevIter) {
1062 // vr::VRControllerState_t aCtrlState = {}; if (myContext->GetControllerState (aCtrlState, aDevIter)) { aCtrlState.ulButtonPressed == 0; }
1067 // =======================================================================
1068 // function : onTrackedDeviceActivated
1070 // =======================================================================
1071 void Aspect_OpenVRSession::onTrackedDeviceActivated (Standard_Integer theDeviceIndex)
1073 Message::SendTrace (TCollection_AsciiString ("OpenVR, Device ") + theDeviceIndex + " attached");
1076 // =======================================================================
1077 // function : onTrackedDeviceDeactivated
1079 // =======================================================================
1080 void Aspect_OpenVRSession::onTrackedDeviceDeactivated (Standard_Integer theDeviceIndex)
1082 Message::SendTrace (TCollection_AsciiString ("OpenVR, Device ") + theDeviceIndex + " detached");
1085 // =======================================================================
1086 // function : onTrackedDeviceUpdated
1088 // =======================================================================
1089 void Aspect_OpenVRSession::onTrackedDeviceUpdated (Standard_Integer theDeviceIndex)
1091 Message::SendTrace (TCollection_AsciiString ("OpenVR, Device ") + theDeviceIndex + " updated");
1094 // =======================================================================
1095 // function : SubmitEye
1097 // =======================================================================
1098 bool Aspect_OpenVRSession::SubmitEye (void* theTexture,
1099 Aspect_GraphicsLibrary theGraphicsLib,
1100 Aspect_ColorSpace theColorSpace,
1103 if (theTexture == NULL)
1108 if (myContext->System == NULL)
1113 vr::Texture_t aVRTexture = { (void* )theTexture, vr::TextureType_OpenGL, vr::ColorSpace_Gamma };
1114 switch (theGraphicsLib)
1116 case Aspect_GraphicsLibrary_OpenGL:
1117 aVRTexture.eType = vr::TextureType_OpenGL;
1120 Message::SendFail ("Compositor error: unsupported Graphics API");
1123 switch (theColorSpace)
1125 case Aspect_ColorSpace_sRGB: aVRTexture.eColorSpace = vr::ColorSpace_Gamma; break;
1126 case Aspect_ColorSpace_Linear: aVRTexture.eColorSpace = vr::ColorSpace_Linear; break;
1129 const vr::EVRCompositorError aVRError = vr::VRCompositor()->Submit (theEye == Aspect_Eye_Right ? vr::Eye_Right : vr::Eye_Left, &aVRTexture);
1130 if (aVRError != vr::VRCompositorError_None)
1132 if (aVRError != vr::VRCompositorError_AlreadySubmitted)
1134 Message::SendFail (TCollection_AsciiString ("Compositor Error: ") + getVRCompositorError (aVRError));
1138 Message::SendTrace (TCollection_AsciiString ("Compositor Error: ") + getVRCompositorError (aVRError));
1144 (void )theGraphicsLib;
1145 (void )theColorSpace;