b40cdc2b |
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 | { |
a87b1b37 |
230 | Message::SendFail (TCollection_AsciiString ("OpenVR, Unable to load render model texture: ") + theVrModelName + " - " + int(aVrError)); |
b40cdc2b |
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. |
faff3767 |
264 | virtual Handle(Image_PixMap) ReadImage (const Handle(Image_SupportedFormats)& ) const Standard_OVERRIDE |
b40cdc2b |
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; |
a87b1b37 |
475 | Message::SendFail (TCollection_AsciiString ("OpenVR, Unable to init VR runtime: ") + vr::VR_GetVRInitErrorAsEnglishDescription (aVrError)); |
b40cdc2b |
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 | { |
a87b1b37 |
483 | Message::SendFail (TCollection_AsciiString ("Unable to get render model interface: ") + vr::VR_GetVRInitErrorAsEnglishDescription (aVrError));; |
b40cdc2b |
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 |
a87b1b37 |
500 | Message::SendFail ("Open CASCADE has been built without OpenVR support"); |
b40cdc2b |
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 | { |
a87b1b37 |
519 | Message::SendFail (TCollection_AsciiString ("OpenVR, Unable to open actions manifest '") + myActionsManifest + "'"); |
b40cdc2b |
520 | return false; |
521 | } |
522 | |
523 | vr::EVRInputError aVrError = vr::VRInput()->SetActionManifestPath (myActionsManifest.ToCString()); |
524 | if (aVrError != vr::VRInputError_None) |
525 | { |
a87b1b37 |
526 | Message::SendFail (TCollection_AsciiString ("OpenVR, Unable to load actions manifest '") + myActionsManifest |
527 | + "': " + getVRInputError (aVrError)); |
b40cdc2b |
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; |
a87b1b37 |
547 | Message::SendFail (TCollection_AsciiString ("OpenVR, Unable to load action '") + anAction->Id() + "' from '" + myActionsManifest |
548 | + "': " + getVRInputError (aVrError)); |
b40cdc2b |
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; |
a87b1b37 |
561 | Message::SendFail (TCollection_AsciiString ("OpenVR, Unable to load action set '") + anActionSet->Id() + "' from '" + myActionsManifest |
562 | + "': " + getVRInputError (aVrError)); |
b40cdc2b |
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 | { |
a87b1b37 |
661 | Message::SendFail (TCollection_AsciiString ("OpenVR, Unable to load render model: ") + aRenderModelName + " - " + int(aVrError)); |
b40cdc2b |
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 | { |
a87b1b37 |
820 | Message::SendTrace (TCollection_AsciiString ("Compositor wait poses error: ") + getVRCompositorError (aVRError)); |
b40cdc2b |
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 | { |
a87b1b37 |
861 | Message::SendFail (TCollection_AsciiString ("Input Error on '") + theAction->Id() + "': " + getVRInputError (anInErr)); |
b40cdc2b |
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 | { |
a87b1b37 |
896 | Message::SendFail (TCollection_AsciiString ("Input Error on '") + theAction->Id() + "': " + getVRInputError (anInErr)); |
b40cdc2b |
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 | { |
a87b1b37 |
937 | Message::SendFail (TCollection_AsciiString ("Input Error on '") + theAction->Id() + "': " + getVRInputError (anInErr)); |
b40cdc2b |
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 | { |
a87b1b37 |
979 | Message::SendFail (TCollection_AsciiString ("Output Error on '") + theAction->Id() + "': " + getVRInputError (anInErr)); |
b40cdc2b |
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 | { |
a87b1b37 |
1073 | Message::SendTrace (TCollection_AsciiString ("OpenVR, Device ") + theDeviceIndex + " attached"); |
b40cdc2b |
1074 | } |
1075 | |
1076 | // ======================================================================= |
1077 | // function : onTrackedDeviceDeactivated |
1078 | // purpose : |
1079 | // ======================================================================= |
1080 | void Aspect_OpenVRSession::onTrackedDeviceDeactivated (Standard_Integer theDeviceIndex) |
1081 | { |
a87b1b37 |
1082 | Message::SendTrace (TCollection_AsciiString ("OpenVR, Device ") + theDeviceIndex + " detached"); |
b40cdc2b |
1083 | } |
1084 | |
1085 | // ======================================================================= |
1086 | // function : onTrackedDeviceUpdated |
1087 | // purpose : |
1088 | // ======================================================================= |
1089 | void Aspect_OpenVRSession::onTrackedDeviceUpdated (Standard_Integer theDeviceIndex) |
1090 | { |
a87b1b37 |
1091 | Message::SendTrace (TCollection_AsciiString ("OpenVR, Device ") + theDeviceIndex + " updated"); |
b40cdc2b |
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: |
a87b1b37 |
1120 | Message::SendFail ("Compositor error: unsupported Graphics API"); |
b40cdc2b |
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 | { |
a87b1b37 |
1134 | Message::SendFail (TCollection_AsciiString ("Compositor Error: ") + getVRCompositorError (aVRError)); |
b40cdc2b |
1135 | } |
1136 | else |
1137 | { |
a87b1b37 |
1138 | Message::SendTrace (TCollection_AsciiString ("Compositor Error: ") + getVRCompositorError (aVRError)); |
b40cdc2b |
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 | } |