bd5888c9fa49a5e5445761f798dccf0f6d9f4705
[occt.git] / src / Aspect / Aspect_OpenVRSession.cxx
1 // Copyright (c) 2020 OPEN CASCADE SAS
2 //
3 // This file is part of Open CASCADE Technology software library.
4 //
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.
10 //
11 // Alternatively, this file may be used under the terms of Open CASCADE
12 // commercial license or contractual agreement.
13
14 #include <Aspect_OpenVRSession.hxx>
15
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>
22 #include <OSD.hxx>
23 #include <OSD_Environment.hxx>
24 #include <OSD_File.hxx>
25 #include <OSD_Process.hxx>
26
27 #ifdef HAVE_OPENVR
28   #include <openvr.h>
29
30 namespace
31 {
32   //! Print OpenVR compositor error.
33   static const char* getVRCompositorError (vr::EVRCompositorError theVRError)
34   {
35     switch (theVRError)
36     {
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";
50     }
51     return "UNKNOWN";
52   }
53
54   //! Print OpenVR input error.
55   static const char* getVRInputError (vr::EVRInputError theVRError)
56   {
57     switch (theVRError)
58     {
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";
80     }
81     return "UNKNOWN";
82   }
83
84   //! Convert OpenVR mat4x4 into OCCT mat4x4.
85   static NCollection_Mat4<double> mat44vr2Occ (const vr::HmdMatrix44_t& theMat4)
86   {
87     NCollection_Mat4<double> aMat4;
88     for (int aRow = 0; aRow < 4; ++aRow)
89     {
90       aMat4.SetRow (aRow, NCollection_Vec4<double> (theMat4.m[aRow][0], theMat4.m[aRow][1], theMat4.m[aRow][2], theMat4.m[aRow][3]));
91     }
92     return aMat4;
93   }
94
95   //! Convert OpenVR mat3x4 into OCCT gp_Trsf.
96   static gp_Trsf mat34vr2OccTrsf (const vr::HmdMatrix34_t& theMat4)
97   {
98     gp_Trsf aTrsf;
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]);
102     return aTrsf;
103   }
104
105   //! Convert OpenVR tracked pose.
106   static Aspect_TrackedDevicePose poseVr2Occ (const vr::TrackedDevicePose_t& theVrPose,
107                                               const Standard_Real theUnitFactor)
108   {
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)
115     {
116       aPose.Orientation = mat34vr2OccTrsf (theVrPose.mDeviceToAbsoluteTracking);
117       if (theUnitFactor != 1.0)
118       {
119         aPose.Orientation.SetTranslationPart (aPose.Orientation.TranslationPart() * theUnitFactor);
120       }
121     }
122     return aPose;
123   }
124
125   //! Find location of default actions manifest file (based on CSF_OCCTResourcePath or CASROOT variables).
126   TCollection_AsciiString defaultActionsManifestInit()
127   {
128     const TCollection_AsciiString THE_ACTIONS_JSON = "occtvr_actions.json";
129     const TCollection_AsciiString aResRoot (OSD_Environment ("CSF_OCCTResourcePath").Value());
130     if (!aResRoot.IsEmpty())
131     {
132       if (OSD_File (aResRoot + "/" + THE_ACTIONS_JSON).Exists())
133       {
134         return aResRoot + "/" + THE_ACTIONS_JSON;
135       }
136       if (OSD_File (aResRoot + "/XRResources/" + THE_ACTIONS_JSON).Exists())
137       {
138         return aResRoot + "/XRResources/" + THE_ACTIONS_JSON;
139       }
140     }
141     const TCollection_AsciiString aCasRoot (OSD_Environment ("CASROOT").Value());
142     if (!aCasRoot.IsEmpty())
143     {
144       if (OSD_File (aCasRoot + "/" + THE_ACTIONS_JSON).Exists())
145       {
146         return aCasRoot + "/" + THE_ACTIONS_JSON;
147       }
148       if (OSD_File (aCasRoot + "/XRResources/" + THE_ACTIONS_JSON).Exists())
149       {
150         return aCasRoot + "/XRResources/" + THE_ACTIONS_JSON;
151       }
152       if (OSD_File (aCasRoot + "/XRResources/src/" + THE_ACTIONS_JSON).Exists())
153       {
154         return aCasRoot + "/XRResources/src/" + THE_ACTIONS_JSON;
155       }
156     }
157     return OSD_Process::ExecutablePath() + "/occtvr_actions.json";
158   }
159 }
160 #endif
161
162 IMPLEMENT_STANDARD_RTTIEXT(Aspect_OpenVRSession, Aspect_XRSession)
163
164 struct Aspect_OpenVRSession::VRContext
165 {
166 #ifdef HAVE_OPENVR
167   vr::TrackedDevicePose_t TrackedPoses[vr::k_unMaxTrackedDeviceCount]; //!< array of tracked devices poses
168   vr::IVRSystem*          System; //!< OpenVR session object
169
170   //! Empty constructor.
171   Aspect_OpenVRSession::VRContext() : System (NULL)
172   {
173     memset (TrackedPoses, 0, sizeof(TrackedPoses));
174   }
175
176   //! IVRSystem::PollNextEvent() wrapper.
177   bool PollNextEvent (vr::VREvent_t& theEvent)
178   {
179     return System->PollNextEvent (&theEvent, sizeof(vr::VREvent_t));
180   }
181
182   //! IVRSystem::GetControllerState() wrapper.
183   bool GetControllerState (vr::VRControllerState_t& theState,
184                            vr::TrackedDeviceIndex_t theDevice)
185   {
186     return System->GetControllerState (theDevice, &theState, sizeof(vr::VRControllerState_t&));
187   }
188
189   //! Retrieve string property from OpenVR.
190   TCollection_AsciiString getVrTrackedDeviceString (vr::TrackedDeviceIndex_t  theDevice,
191                                                     vr::TrackedDeviceProperty theProperty,
192                                                     vr::TrackedPropertyError* theError = NULL)
193   {
194     const uint32_t aBuffLen = System->GetStringTrackedDeviceProperty(theDevice, theProperty, NULL, 0, theError);
195     if (aBuffLen == 0)
196     {
197       return TCollection_AsciiString();
198     }
199
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);
204     return aResult;
205   }
206 #endif
207 };
208
209 #ifdef HAVE_OPENVR
210 //! Image wrapping vr::RenderModel_TextureMap_t.
211 class Aspect_OpenVRSession::VRImagePixmap : public Image_PixMap
212 {
213 public:
214   //! Empty constructor.
215   VRImagePixmap() : myVrTexture (NULL) {}
216
217   //! Load the texture.
218   bool Load (vr::TextureID_t theTexture, const TCollection_AsciiString& theVrModelName)
219   {
220     vr::RenderModel_TextureMap_t* aVrTexture = NULL;
221     vr::EVRRenderModelError aVrError = vr::VRRenderModelError_Loading;
222     for (; aVrError == vr::VRRenderModelError_Loading;)
223     {
224       aVrError = vr::VRRenderModels()->LoadTexture_Async (theTexture, &aVrTexture);
225       OSD::MilliSecSleep (1);
226     }
227     if (aVrError != vr::VRRenderModelError_None
228      || aVrTexture == NULL)
229     {
230       Message::SendFail (TCollection_AsciiString ("OpenVR, Unable to load render model texture: ") + theVrModelName + " - " + int(aVrError));
231       return false;
232     }
233
234     InitWrapper (Image_Format_RGBA, (Standard_Byte* )aVrTexture->rubTextureMapData, aVrTexture->unWidth, aVrTexture->unHeight);
235     myVrTexture = aVrTexture;
236     return true;
237   }
238
239   virtual ~VRImagePixmap()
240   {
241     if (myVrTexture != NULL)
242     {
243       vr::VRRenderModels()->FreeTexture (myVrTexture);
244     }
245   }
246 private:
247   vr::RenderModel_TextureMap_t* myVrTexture;
248 };
249
250 //! Image_Texture extension using vr::VRRenderModels().
251 class Aspect_OpenVRSession::VRTextureSource : public Image_Texture
252 {
253 public:
254
255   //! Main constructor.
256   VRTextureSource (vr::TextureID_t theTextureId, const TCollection_AsciiString& theVrModelName)
257   : Image_Texture (""), myVrTextureId (theTextureId), myVrModelName (theVrModelName)
258   {
259     myTextureId = TCollection_AsciiString ("texturevr://map_#") + (int )theTextureId;
260   }
261
262 protected:
263   //! Read image.
264   virtual Handle(Image_PixMap) ReadImage() const Standard_OVERRIDE
265   {
266     Handle(VRImagePixmap) aPixmap = new VRImagePixmap();
267     if (!aPixmap->Load (myVrTextureId, myVrModelName))
268     {
269       return Handle(VRImagePixmap)();
270     }
271     return aPixmap;
272   }
273 private:
274   vr::TextureID_t         myVrTextureId;
275   TCollection_AsciiString myVrModelName;
276 };
277 #endif
278
279 // =======================================================================
280 // function : IsHmdPresent
281 // purpose  :
282 // =======================================================================
283 bool Aspect_OpenVRSession::IsHmdPresent()
284 {
285 #ifdef HAVE_OPENVR
286   return vr::VR_IsHmdPresent();
287 #else
288   return false;
289 #endif
290 }
291
292 // =======================================================================
293 // function : defaultActionsManifest
294 // purpose  :
295 // =======================================================================
296 TCollection_AsciiString Aspect_OpenVRSession::defaultActionsManifest()
297 {
298 #ifdef HAVE_OPENVR
299   static const TCollection_AsciiString THE_ACTIONS_JSON_FULL = defaultActionsManifestInit();
300   return THE_ACTIONS_JSON_FULL;
301 #else
302   return TCollection_AsciiString();
303 #endif
304 }
305
306 // =======================================================================
307 // function : Aspect_OpenVRSession
308 // purpose  :
309 // =======================================================================
310 Aspect_OpenVRSession::Aspect_OpenVRSession()
311 : myContext (new VRContext())
312 {
313 #ifdef HAVE_OPENVR
314   myActionsManifest = defaultActionsManifest();
315   myTrackedPoses.Resize (0, (Standard_Integer )vr::k_unMaxTrackedDeviceCount - 1, false);
316   {
317     Handle(Aspect_XRActionSet) aHeadActionSet = new Aspect_XRActionSet ("/actions/generic_head");
318     myActionSets.Add (aHeadActionSet->Id(), aHeadActionSet);
319
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;
324   }
325   for (int aHand = 0; aHand < 2; ++aHand)
326   {
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);
330
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;
334
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;
338
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;
342
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;
346
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;
350
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;
354
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;
358
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;
362
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;
366
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;
370
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;
374
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;
378
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;
382
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;
386
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;
390
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;
394   }
395 #endif
396 }
397
398 // =======================================================================
399 // function : ~Aspect_OpenVRSession
400 // purpose  :
401 // =======================================================================
402 Aspect_OpenVRSession::~Aspect_OpenVRSession()
403 {
404   closeVR();
405   delete myContext;
406 }
407
408 // =======================================================================
409 // function : closeVR
410 // purpose  :
411 // =======================================================================
412 void Aspect_OpenVRSession::closeVR()
413 {
414 #ifdef HAVE_OPENVR
415   if (myContext->System != NULL)
416   {
417     vr::VR_Shutdown();
418     myContext->System = NULL;
419   }
420 #endif
421 }
422
423 // =======================================================================
424 // function : getVRSystem
425 // purpose  :
426 // =======================================================================
427 void* Aspect_OpenVRSession::getVRSystem() const
428 {
429 #ifdef HAVE_OPENVR
430   return myContext->System;
431 #else
432   return NULL;
433 #endif
434 }
435
436 // =======================================================================
437 // function : Close
438 // purpose  :
439 // =======================================================================
440 void Aspect_OpenVRSession::Close()
441 {
442   closeVR();
443 }
444
445 // =======================================================================
446 // function : IsOpen
447 // purpose  :
448 // =======================================================================
449 bool Aspect_OpenVRSession::IsOpen() const
450 {
451 #ifdef HAVE_OPENVR
452   return myContext->System != NULL;
453 #else
454   return false;
455 #endif
456 }
457
458 // =======================================================================
459 // function : Open
460 // purpose  :
461 // =======================================================================
462 bool Aspect_OpenVRSession::Open()
463 {
464 #ifdef HAVE_OPENVR
465   if (myContext->System != NULL)
466   {
467     return true;
468   }
469
470   vr::EVRInitError aVrError = vr::VRInitError_None;
471   myContext->System = vr::VR_Init (&aVrError, vr::VRApplication_Scene);
472   if (aVrError != vr::VRInitError_None)
473   {
474     myContext->System = NULL;
475     Message::SendFail (TCollection_AsciiString ("OpenVR, Unable to init VR runtime: ") + vr::VR_GetVRInitErrorAsEnglishDescription (aVrError));
476     Close();
477     return false;
478   }
479
480   /*vr::IVRRenderModels* aRenderModels = (vr::IVRRenderModels* )vr::VR_GetGenericInterface (vr::IVRRenderModels_Version, &aVrError);
481   if (aRenderModels == NULL)
482   {
483     Message::SendFail (TCollection_AsciiString ("Unable to get render model interface: ") + vr::VR_GetVRInitErrorAsEnglishDescription (aVrError));;
484   }*/
485
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)
492   {
493     Close();
494     return false;
495   }
496   updateProjectionFrustums();
497   initInput();
498   return true;
499 #else
500   Message::SendFail ("Open CASCADE has been built without OpenVR support");
501   return false;
502 #endif
503 }
504
505 // =======================================================================
506 // function : initInput
507 // purpose  :
508 // =======================================================================
509 bool Aspect_OpenVRSession::initInput()
510 {
511 #ifdef HAVE_OPENVR
512   if (myContext->System == NULL)
513   {
514     return false;
515   }
516
517   if (!OSD_File (myActionsManifest).Exists())
518   {
519     Message::SendFail (TCollection_AsciiString ("OpenVR, Unable to open actions manifest '") + myActionsManifest  + "'");
520     return false;
521   }
522
523   vr::EVRInputError aVrError = vr::VRInput()->SetActionManifestPath (myActionsManifest.ToCString());
524   if (aVrError != vr::VRInputError_None)
525   {
526     Message::SendFail (TCollection_AsciiString ("OpenVR, Unable to load actions manifest '") + myActionsManifest
527                      + "': " + getVRInputError (aVrError));
528     return false;
529   }
530
531   bool hasErrors = false;
532   for (Aspect_XRActionSetMap::Iterator aSetIter (myActionSets); aSetIter.More(); aSetIter.Next())
533   {
534     const Handle(Aspect_XRActionSet)& anActionSet = aSetIter.Value();
535     for (Aspect_XRActionMap::Iterator anActionIter (anActionSet->Actions()); anActionIter.More(); anActionIter.Next())
536     {
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)
541       {
542         anAction->SetRawHandle (anActionHandle);
543       }
544       else
545       {
546         hasErrors = true;
547         Message::SendFail (TCollection_AsciiString ("OpenVR, Unable to load action '") + anAction->Id() + "' from '" + myActionsManifest
548                          + "': " + getVRInputError (aVrError));
549       }
550     }
551
552     vr::VRActionSetHandle_t anActionSetHandle = 0;
553     aVrError = vr::VRInput()->GetActionSetHandle (anActionSet->Id().ToCString(), &anActionSetHandle);
554     if (aVrError == vr::VRInputError_None)
555     {
556       anActionSet->SetRawHandle (anActionSetHandle);
557     }
558     else
559     {
560       hasErrors = true;
561       Message::SendFail (TCollection_AsciiString ("OpenVR, Unable to load action set '") + anActionSet->Id() + "' from '" + myActionsManifest
562                        + "': " + getVRInputError (aVrError));
563     }
564   }
565   return !hasErrors;
566 #else
567   return false;
568 #endif
569 }
570
571 // =======================================================================
572 // function : GetString
573 // purpose  :
574 // =======================================================================
575 TCollection_AsciiString Aspect_OpenVRSession::GetString (InfoString theInfo) const
576 {
577 #ifdef HAVE_OPENVR
578   if (myContext->System != NULL)
579   {
580     vr::ETrackedDeviceProperty aVrProp = vr::Prop_Invalid;
581     switch (theInfo)
582     {
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;
587     }
588     if (aVrProp != vr::Prop_Invalid)
589     {
590       return myContext->getVrTrackedDeviceString (vr::k_unTrackedDeviceIndex_Hmd, aVrProp);
591     }
592   }
593 #else
594   (void )theInfo;
595 #endif
596   return TCollection_AsciiString();
597 }
598
599 // =======================================================================
600 // function : NamedTrackedDevice
601 // purpose  :
602 // =======================================================================
603 Standard_Integer Aspect_OpenVRSession::NamedTrackedDevice (Aspect_XRTrackedDeviceRole theDevice) const
604 {
605 #ifdef HAVE_OPENVR
606   if (myContext->System != NULL)
607   {
608     vr::TrackedDeviceIndex_t aDevIndex = vr::k_unTrackedDeviceIndexInvalid;
609     switch (theDevice)
610     {
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;
615     }
616     if (aDevIndex == vr::k_unTrackedDeviceIndexInvalid)
617     {
618       return -1;
619     }
620     return (Standard_Integer )aDevIndex;
621   }
622 #else
623   (void )theDevice;
624 #endif
625   return -1;
626 }
627
628 // =======================================================================
629 // function : loadRenderModel
630 // purpose  :
631 // =======================================================================
632 Handle(Graphic3d_ArrayOfTriangles) Aspect_OpenVRSession::loadRenderModel (Standard_Integer theDevice,
633                                                                           Standard_Boolean theToApplyUnitFactor,
634                                                                           Handle(Image_Texture)& theTexture)
635 {
636   if (theDevice < 0)
637   {
638     return Handle(Graphic3d_ArrayOfTriangles)();
639   }
640 #ifdef HAVE_OPENVR
641   if (myContext->System == NULL)
642   {
643     return Handle(Graphic3d_ArrayOfTriangles)();
644   }
645
646   const TCollection_AsciiString aRenderModelName = myContext->getVrTrackedDeviceString (theDevice, vr::Prop_RenderModelName_String);
647   if (aRenderModelName.IsEmpty())
648   {
649     return Handle(Graphic3d_ArrayOfTriangles)();
650   }
651
652   vr::RenderModel_t* aVrModel = NULL;
653   vr::EVRRenderModelError aVrError = vr::VRRenderModelError_Loading;
654   for (; aVrError == vr::VRRenderModelError_Loading;)
655   {
656     aVrError = vr::VRRenderModels()->LoadRenderModel_Async (aRenderModelName.ToCString(), &aVrModel);
657     OSD::MilliSecSleep (1);
658   }
659   if (aVrError != vr::VRRenderModelError_None)
660   {
661     Message::SendFail (TCollection_AsciiString ("OpenVR, Unable to load render model: ") + aRenderModelName + " - " + int(aVrError));
662     return Handle(Graphic3d_ArrayOfTriangles)();
663   }
664
665   if (aVrModel->diffuseTextureId >= 0)
666   {
667     theTexture = new VRTextureSource (aVrModel->diffuseTextureId, aRenderModelName);
668   }
669
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)
674   {
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]);
679   }
680   for (uint32_t aTriIter = 0; aTriIter < aVrModel->unTriangleCount; ++aTriIter)
681   {
682     aTris->AddEdges (aVrModel->rIndexData[aTriIter * 3 + 0] + 1,
683                      aVrModel->rIndexData[aTriIter * 3 + 1] + 1,
684                      aVrModel->rIndexData[aTriIter * 3 + 2] + 1);
685   }
686
687   vr::VRRenderModels()->FreeRenderModel (aVrModel);
688   return aTris;
689 #else
690   (void )theToApplyUnitFactor;
691   (void )theTexture;
692   return Handle(Graphic3d_ArrayOfTriangles)();
693 #endif
694 }
695
696 // =======================================================================
697 // function : EyeToHeadTransform
698 // purpose  :
699 // =======================================================================
700 NCollection_Mat4<double> Aspect_OpenVRSession::EyeToHeadTransform (Aspect_Eye theEye) const
701 {
702 #ifdef HAVE_OPENVR
703   if (myContext->System != NULL)
704   {
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)
708     {
709       aTrsf.SetTranslationPart (aTrsf.TranslationPart() * myUnitFactor);
710     }
711     NCollection_Mat4<double> aMat4;
712     aTrsf.GetMat4 (aMat4);
713     return aMat4;
714   }
715 #else
716   (void )theEye;
717 #endif
718   return NCollection_Mat4<double>();
719 }
720
721 // =======================================================================
722 // function : ProjectionMatrix
723 // purpose  :
724 // =======================================================================
725 NCollection_Mat4<double> Aspect_OpenVRSession::ProjectionMatrix (Aspect_Eye theEye,
726                                                                  double theZNear,
727                                                                  double theZFar) const
728 {
729 #ifdef HAVE_OPENVR
730   if (myContext->System != NULL)
731   {
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);
735   }
736 #else
737   (void )theEye;
738   (void )theZNear;
739   (void )theZFar;
740 #endif
741   return NCollection_Mat4<double>();
742 }
743
744 // =======================================================================
745 // function : updateProjectionFrustums
746 // purpose  :
747 // =======================================================================
748 void Aspect_OpenVRSession::updateProjectionFrustums()
749 {
750 #ifdef HAVE_OPENVR
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);
758
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;
763
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;
770 #endif
771 }
772
773 // =======================================================================
774 // function : SetTrackingOrigin
775 // purpose  :
776 // =======================================================================
777 void Aspect_OpenVRSession::SetTrackingOrigin (TrackingUniverseOrigin theOrigin)
778 {
779 #ifdef HAVE_OPENVR
780   if (myContext->System != NULL)
781   {
782     vr::ETrackingUniverseOrigin anOrigin = vr::TrackingUniverseStanding;
783     switch (theOrigin)
784     {
785       case TrackingUniverseOrigin_Seated:   anOrigin = vr::TrackingUniverseSeated;   break;
786       case TrackingUniverseOrigin_Standing: anOrigin = vr::TrackingUniverseStanding; break;
787     }
788     vr::VRCompositor()->SetTrackingSpace (anOrigin);
789   }
790 #endif
791   myTrackOrigin = theOrigin;
792 }
793
794 // =======================================================================
795 // function : WaitPoses
796 // purpose  :
797 // =======================================================================
798 bool Aspect_OpenVRSession::WaitPoses()
799 {
800 #ifdef HAVE_OPENVR
801   if (myContext->System == NULL)
802   {
803     return false;
804   }
805
806   switch (vr::VRCompositor()->GetTrackingSpace())
807   {
808     case vr::TrackingUniverseSeated:
809       myTrackOrigin = TrackingUniverseOrigin_Seated;
810       break;
811     case vr::TrackingUniverseRawAndUncalibrated:
812     case vr::TrackingUniverseStanding:
813       myTrackOrigin = TrackingUniverseOrigin_Standing;
814       break;
815   }
816
817   const vr::EVRCompositorError aVRError = vr::VRCompositor()->WaitGetPoses (myContext->TrackedPoses, vr::k_unMaxTrackedDeviceCount, NULL, 0);
818   if (aVRError != vr::VRCompositorError_None)
819   {
820     Message::SendTrace (TCollection_AsciiString ("Compositor wait poses error: ") + getVRCompositorError (aVRError));
821   }
822
823   for (Standard_Integer aPoseIter = myTrackedPoses.Lower(); aPoseIter <= myTrackedPoses.Upper(); ++aPoseIter)
824   {
825     const vr::TrackedDevicePose_t& aVrPose = myContext->TrackedPoses[aPoseIter];
826     myTrackedPoses[aPoseIter] = poseVr2Occ (aVrPose, myUnitFactor);
827   }
828
829   if (myContext->TrackedPoses[vr::k_unTrackedDeviceIndex_Hmd].bPoseIsValid)
830   {
831     myHeadPose = myTrackedPoses[vr::k_unTrackedDeviceIndex_Hmd].Orientation;
832     updateProjectionFrustums();
833   }
834   return aVRError != vr::VRCompositorError_None;
835 #else
836   return false;
837 #endif
838 }
839
840 // =======================================================================
841 // function : GetDigitalActionData
842 // purpose  :
843 // =======================================================================
844 Aspect_XRDigitalActionData Aspect_OpenVRSession::GetDigitalActionData (const Handle(Aspect_XRAction)& theAction) const
845 {
846   if (theAction.IsNull()
847    || theAction->Type() != Aspect_XRActionType_InputDigital)
848   {
849     throw Standard_ProgramError("Aspect_OpenVRSession::GetDigitalActionData() called for wrong action");
850   }
851
852   Aspect_XRDigitalActionData anActionData;
853 #ifdef HAVE_OPENVR
854   if (myContext->System != NULL
855    && theAction->RawHandle() != 0)
856   {
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)
860     {
861       Message::SendFail (TCollection_AsciiString ("Input Error on '") + theAction->Id() + "': " + getVRInputError (anInErr));
862       return anActionData;
863     }
864
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;
870   }
871 #endif
872   return anActionData;
873 }
874
875 // =======================================================================
876 // function : GetAnalogActionData
877 // purpose  :
878 // =======================================================================
879 Aspect_XRAnalogActionData Aspect_OpenVRSession::GetAnalogActionData (const Handle(Aspect_XRAction)& theAction) const
880 {
881   if (theAction.IsNull()
882    || theAction->Type() != Aspect_XRActionType_InputAnalog)
883   {
884     throw Standard_ProgramError("Aspect_OpenVRSession::GetAnalogActionData() called for wrong action");
885   }
886
887   Aspect_XRAnalogActionData anActionData;
888 #ifdef HAVE_OPENVR
889   if (myContext->System != NULL
890    && theAction->RawHandle() != 0)
891   {
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)
895     {
896       Message::SendFail (TCollection_AsciiString ("Input Error on '") + theAction->Id() + "': " + getVRInputError (anInErr));
897       return anActionData;
898     }
899
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;
905   }
906 #endif
907   return anActionData;
908 }
909
910 // =======================================================================
911 // function : GetPoseActionDataForNextFrame
912 // purpose  :
913 // =======================================================================
914 Aspect_XRPoseActionData Aspect_OpenVRSession::GetPoseActionDataForNextFrame (const Handle(Aspect_XRAction)& theAction) const
915 {
916   if (theAction.IsNull()
917    || theAction->Type() != Aspect_XRActionType_InputPose)
918   {
919     throw Standard_ProgramError("Aspect_OpenVRSession::GetPoseActionDataForNextFrame() called for wrong action");
920   }
921
922   Aspect_XRPoseActionData anActionData;
923 #ifdef HAVE_OPENVR
924   if (myContext->System != NULL
925    && theAction->RawHandle() != 0)
926   {
927     vr::ETrackingUniverseOrigin anOrigin = vr::TrackingUniverseSeated;
928     switch (myTrackOrigin)
929     {
930       case TrackingUniverseOrigin_Seated:   anOrigin = vr::TrackingUniverseSeated;   break;
931       case TrackingUniverseOrigin_Standing: anOrigin = vr::TrackingUniverseStanding; break;
932     }
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)
936     {
937       Message::SendFail (TCollection_AsciiString ("Input Error on '") + theAction->Id() + "': " + getVRInputError (anInErr));
938       return anActionData;
939     }
940
941     anActionData.Pose = poseVr2Occ (aNewData.pose, myUnitFactor);
942     anActionData.IsActive = aNewData.bActive;
943     anActionData.ActiveOrigin = aNewData.activeOrigin;
944   }
945 #endif
946   return anActionData;
947 }
948
949 // =======================================================================
950 // function : triggerHapticVibrationAction
951 // purpose  :
952 // =======================================================================
953 void Aspect_OpenVRSession::triggerHapticVibrationAction (const Handle(Aspect_XRAction)& theAction,
954                                                          const Aspect_XRHapticActionData& theParams)
955 {
956   if (theAction.IsNull()
957    || theAction->Type() != Aspect_XRActionType_OutputHaptic)
958   {
959     throw Standard_ProgramError("Aspect_OpenVRSession::triggerHapticVibrationAction() called for wrong action");
960   }
961
962 #ifdef HAVE_OPENVR
963   if (myContext->System != NULL
964    && theAction->RawHandle() != 0)
965   {
966     Aspect_XRHapticActionData aParams = theParams;
967     if (!theParams.IsValid())
968     {
969       // preset for aborting
970       aParams.Duration  = 0.0f;
971       aParams.Frequency = 1.0f;
972       aParams.Amplitude = 0.1f;
973     }
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)
978     {
979       Message::SendFail (TCollection_AsciiString ("Output Error on '") + theAction->Id() + "': " + getVRInputError (anInErr));
980     }
981   }
982 #else
983   (void )theParams;
984 #endif
985 }
986
987 // =======================================================================
988 // function : ProcessEvents
989 // purpose  :
990 // =======================================================================
991 void Aspect_OpenVRSession::ProcessEvents()
992 {
993 #ifdef HAVE_OPENVR
994   if (myContext->System == NULL)
995   {
996     return;
997   }
998
999   // process OpenVR events
1000   vr::VREvent_t aVREvent = {};
1001   for (; myContext->PollNextEvent (aVREvent);)
1002   {
1003     switch (aVREvent.eventType)
1004     {
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;
1008     }
1009   }
1010
1011   // process OpenVR action state
1012   if (myActionSets.Extent() > 0)
1013   {
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)
1017     {
1018       anActionSets[aSetIter].ulActionSet = myActionSets.FindFromIndex (aSetIter + 1)->RawHandle();
1019     }
1020     vr::VRInput()->UpdateActionState (anActionSets, sizeof(vr::VRActiveActionSet_t), myActionSets.Extent());
1021   }
1022
1023   WaitPoses();
1024
1025   for (Aspect_XRActionSetMap::Iterator aSetIter (myActionSets); aSetIter.More(); aSetIter.Next())
1026   {
1027     const Handle(Aspect_XRActionSet)& anActionSet = aSetIter.Value();
1028     for (Aspect_XRActionMap::Iterator anActionIter (anActionSet->Actions()); anActionIter.More(); anActionIter.Next())
1029     {
1030       const Handle(Aspect_XRAction)& anAction = anActionIter.Value();
1031       if (anAction->RawHandle() == 0
1032        || anAction->Id().IsEmpty())
1033       {
1034         continue;
1035       }
1036
1037       switch (anAction->Type())
1038       {
1039         case Aspect_XRActionType_InputDigital:
1040         {
1041           Aspect_XRDigitalActionData aData = GetDigitalActionData (anAction);
1042           //if (aData.IsChanged) { std::cout << "  " << anAction->Id() << " pressed: " << aData.IsPressed << "\n"; }
1043           break;
1044         }
1045         case Aspect_XRActionType_InputAnalog:
1046         {
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"; }
1049           break;
1050         }
1051         case Aspect_XRActionType_InputPose:
1052         {
1053           GetPoseActionDataForNextFrame (anAction);
1054           break;
1055         }
1056       }
1057     }
1058   }
1059
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; }
1063   //}
1064 #endif
1065 }
1066
1067 // =======================================================================
1068 // function : onTrackedDeviceActivated
1069 // purpose  :
1070 // =======================================================================
1071 void Aspect_OpenVRSession::onTrackedDeviceActivated (Standard_Integer theDeviceIndex)
1072 {
1073   Message::SendTrace (TCollection_AsciiString ("OpenVR, Device ") + theDeviceIndex + " attached");
1074 }
1075
1076 // =======================================================================
1077 // function : onTrackedDeviceDeactivated
1078 // purpose  :
1079 // =======================================================================
1080 void Aspect_OpenVRSession::onTrackedDeviceDeactivated (Standard_Integer theDeviceIndex)
1081 {
1082   Message::SendTrace (TCollection_AsciiString ("OpenVR, Device ") + theDeviceIndex + " detached");
1083 }
1084
1085 // =======================================================================
1086 // function : onTrackedDeviceUpdated
1087 // purpose  :
1088 // =======================================================================
1089 void Aspect_OpenVRSession::onTrackedDeviceUpdated (Standard_Integer theDeviceIndex)
1090 {
1091   Message::SendTrace (TCollection_AsciiString ("OpenVR, Device ") + theDeviceIndex + " updated");
1092 }
1093
1094 // =======================================================================
1095 // function : SubmitEye
1096 // purpose  :
1097 // =======================================================================
1098 bool Aspect_OpenVRSession::SubmitEye (void* theTexture,
1099                                       Aspect_GraphicsLibrary theGraphicsLib,
1100                                       Aspect_ColorSpace theColorSpace,
1101                                       Aspect_Eye theEye)
1102 {
1103   if (theTexture == NULL)
1104   {
1105     return false;
1106   }
1107 #ifdef HAVE_OPENVR
1108   if (myContext->System == NULL)
1109   {
1110     return false;
1111   }
1112
1113   vr::Texture_t aVRTexture = { (void* )theTexture, vr::TextureType_OpenGL, vr::ColorSpace_Gamma };
1114   switch (theGraphicsLib)
1115   {
1116     case Aspect_GraphicsLibrary_OpenGL:
1117       aVRTexture.eType = vr::TextureType_OpenGL;
1118       break;
1119     default:
1120       Message::SendFail ("Compositor error: unsupported Graphics API");
1121       return false;
1122   }
1123   switch (theColorSpace)
1124   {
1125     case Aspect_ColorSpace_sRGB:   aVRTexture.eColorSpace = vr::ColorSpace_Gamma;  break;
1126     case Aspect_ColorSpace_Linear: aVRTexture.eColorSpace = vr::ColorSpace_Linear; break;
1127   }
1128
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)
1131   {
1132     if (aVRError != vr::VRCompositorError_AlreadySubmitted)
1133     {
1134       Message::SendFail (TCollection_AsciiString ("Compositor Error: ") + getVRCompositorError (aVRError));
1135     }
1136     else
1137     {
1138       Message::SendTrace (TCollection_AsciiString ("Compositor Error: ") + getVRCompositorError (aVRError));
1139     }
1140     return false;
1141   }
1142   return true;
1143 #else
1144   (void )theGraphicsLib;
1145   (void )theColorSpace;
1146   (void )theEye;
1147   return false;
1148 #endif
1149 }